changeset 1104:46cc4644206d spectrogram-minor-refactor

Convert ColumnNormalization to an enum class, and separate out normalize-visible
author Chris Cannam
date Thu, 14 Jul 2016 16:12:05 +0100
parents d84a0033b305
children ea5ae9dd10ba
files layer/Colour3DPlotLayer.cpp layer/Colour3DPlotLayer.h layer/Colour3DPlotRenderer.h layer/SpectrogramLayer.cpp layer/SpectrogramLayer.h
diffstat 5 files changed, 256 insertions(+), 167 deletions(-) [+]
line wrap: on
line diff
--- a/layer/Colour3DPlotLayer.cpp	Thu Jul 14 15:13:37 2016 +0100
+++ b/layer/Colour3DPlotLayer.cpp	Thu Jul 14 16:12:05 2016 +0100
@@ -57,7 +57,8 @@
     m_colourMap(0),
     m_gain(1.0),
     m_binScale(BinScale::Linear),
-    m_normalization(ColumnOp::NoNormalization),
+    m_normalization(ColumnNormalization::None),
+    m_normalizeVisibleArea(false),
     m_invertVertical(false),
     m_opaque(false),
     m_smooth(false),
@@ -83,6 +84,59 @@
     cacheInvalid(); //!!! dup with above?
 }
 
+ColourScale::Scale
+Colour3DPlotLayer::convertToColourScale(int value)
+{
+    switch (value) {
+    default:
+    case 0: return ColourScale::LinearColourScale;
+    case 1: return ColourScale::LogColourScale;
+    case 2: return ColourScale::PlusMinusOneScale;
+    case 3: return ColourScale::AbsoluteScale;
+    }
+}
+
+int
+Colour3DPlotLayer::convertFromColourScale(ColourScale::Scale scale)
+{
+    switch (scale) {
+    case ColourScale::LinearColourScale: return 0;
+    case ColourScale::LogColourScale: return 1;
+    case ColourScale::PlusMinusOneScale: return 2;
+    case ColourScale::AbsoluteScale: return 3;
+
+    case ColourScale::MeterColourScale:
+    case ColourScale::PhaseColourScale:
+    default: return 0;
+    }
+}
+
+std::pair<ColumnNormalization, bool>
+Colour3DPlotLayer::convertToColumnNorm(int value)
+{
+    switch (value) {
+    default:
+    case 0: return { ColumnNormalization::None, false };
+    case 1: return { ColumnNormalization::Max1, false };
+    case 2: return { ColumnNormalization::None, true }; // visible area
+    case 3: return { ColumnNormalization::Hybrid, false };
+    }
+}
+
+int
+Colour3DPlotLayer::convertFromColumnNorm(ColumnNormalization norm, bool visible)
+{
+    if (visible) return 2;
+    switch (norm) {
+    case ColumnNormalization::None: return 0;
+    case ColumnNormalization::Max1: return 1;
+    case ColumnNormalization::Hybrid: return 3;
+
+    case ColumnNormalization::Sum1:
+    default: return 0;
+    }
+}
+
 void
 Colour3DPlotLayer::setSynchronousPainting(bool synchronous)
 {
@@ -201,8 +255,7 @@
     PropertyList list;
     list.push_back("Colour");
     list.push_back("Colour Scale");
-    list.push_back("Normalize Columns");
-    list.push_back("Normalize Visible Area");
+    list.push_back("Normalization");
     list.push_back("Gain");
     list.push_back("Bin Scale");
     list.push_back("Invert Vertical Scale");
@@ -216,8 +269,7 @@
 {
     if (name == "Colour") return tr("Colour");
     if (name == "Colour Scale") return tr("Scale");
-    if (name == "Normalize Columns") return tr("Normalize Columns");
-    if (name == "Normalize Visible Area") return tr("Normalize Visible Area");
+    if (name == "Normalization") return tr("Normalization");
     if (name == "Invert Vertical Scale") return tr("Invert Vertical Scale");
     if (name == "Gain") return tr("Gain");
     if (name == "Opaque") return tr("Always Opaque");
@@ -229,8 +281,6 @@
 QString
 Colour3DPlotLayer::getPropertyIconName(const PropertyName &name) const
 {
-    if (name == "Normalize Columns") return "normalise-columns";
-    if (name == "Normalize Visible Area") return "normalise";
     if (name == "Invert Vertical Scale") return "invert-vertical";
     if (name == "Opaque") return "opaque";
     if (name == "Smooth") return "smooth";
@@ -241,8 +291,6 @@
 Colour3DPlotLayer::getPropertyType(const PropertyName &name) const
 {
     if (name == "Gain") return RangeProperty;
-    if (name == "Normalize Columns") return ToggleProperty;
-    if (name == "Normalize Visible Area") return ToggleProperty;
     if (name == "Invert Vertical Scale") return ToggleProperty;
     if (name == "Opaque") return ToggleProperty;
     if (name == "Smooth") return ToggleProperty;
@@ -252,9 +300,8 @@
 QString
 Colour3DPlotLayer::getPropertyGroupName(const PropertyName &name) const
 {
-    if (name == "Normalize Columns" ||
-        name == "Normalize Visible Area" ||
-	name == "Colour Scale" ||
+    if (name == "Normalization" ||
+        name == "Colour Scale" ||
         name == "Gain") return tr("Scale");
     if (name == "Bin Scale" ||
         name == "Invert Vertical Scale") return tr("Bins");
@@ -295,7 +342,7 @@
 	*max = 3;
         *deflt = 0;
 
-	val = (int)m_colourScale;
+	val = convertFromColourScale(m_colourScale);
 
     } else if (name == "Colour") {
 
@@ -309,8 +356,9 @@
 	
         *min = 0;
         *max = 3;
-        *deflt = int(ColumnOp::NoNormalization);
-        val = (int)m_normalization;
+        *deflt = 0;
+
+        val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea);
 
     } else if (name == "Invert Vertical Scale") {
 	
@@ -351,7 +399,6 @@
     if (name == "Colour Scale") {
 	switch (value) {
 	default:
-            //!!! yuck
 	case 0: return tr("Linear");
 	case 1: return tr("Log");
 	case 2: return tr("+/-1");
@@ -402,13 +449,7 @@
     if (name == "Gain") {
 	setGain(float(pow(10, value/20.0)));
     } else if (name == "Colour Scale") {
-	switch (value) {
-	default:
-	case 0: setColourScale(ColourScale::LinearColourScale); break;
-	case 1: setColourScale(ColourScale::LogColourScale); break;
-	case 2: setColourScale(ColourScale::PlusMinusOneScale); break;
-	case 3: setColourScale(ColourScale::AbsoluteScale); break;
-	}
+        setColourScale(convertToColourScale(value));
     } else if (name == "Colour") {
         setColourMap(value);
     } else if (name == "Invert Vertical Scale") {
@@ -424,13 +465,9 @@
 	case 1: setBinScale(BinScale::Log); break;
 	}
     } else if (name == "Normalization") {
-        switch (value) {
-        default:
-        case 0: setNormalization(ColumnOp::NoNormalization); break;
-        case 1: setNormalization(ColumnOp::NormalizeColumns); break;
-        case 2: setNormalization(ColumnOp::NormalizeVisibleArea); break;
-        case 3: setNormalization(ColumnOp::NormalizeHybrid); break;
-        }
+        auto n = convertToColumnNorm(value);
+        setNormalization(n.first);
+        setNormalizeVisibleArea(n.second);
     }
 }
 
@@ -484,7 +521,7 @@
 }
 
 void
-Colour3DPlotLayer::setNormalization(ColumnOp::Normalization n)
+Colour3DPlotLayer::setNormalization(ColumnNormalization n)
 {
     if (m_normalization == n) return;
 
@@ -494,13 +531,30 @@
     emit layerParametersChanged();
 }
 
-ColumnOp::Normalization
+ColumnNormalization
 Colour3DPlotLayer::getNormalization() const
 {
     return m_normalization;
 }
 
 void
+Colour3DPlotLayer::setNormalizeVisibleArea(bool n)
+{
+    if (m_normalizeVisibleArea == n) return;
+
+    m_normalizeVisibleArea = n;
+    cacheInvalid();
+    
+    emit layerParametersChanged();
+}
+
+bool
+Colour3DPlotLayer::getNormalizeVisibleArea() const
+{
+    return m_normalizeVisibleArea;
+}
+
+void
 Colour3DPlotLayer::setInvertVertical(bool n)
 {
     if (m_invertVertical == n) return;
@@ -570,7 +624,7 @@
 bool
 Colour3DPlotLayer::isLayerScrollable(const LayerGeometryProvider *v) const
 {
-    if (m_normalization == ColumnOp::NormalizeVisibleArea) {
+    if (m_normalizeVisibleArea) {
         return false;
     }
     if (shouldPaintDenseIn(v)) {
@@ -965,8 +1019,8 @@
 
     DenseThreeDimensionalModel::Column values = m_model->getColumn(col);
     values.resize(m_model->getHeight(), 0.f);
-    if (m_normalization != ColumnOp::NormalizeColumns &&
-        m_normalization != ColumnOp::NormalizeHybrid) {
+    if (m_normalization != ColumnNormalization::Max1 &&
+        m_normalization != ColumnNormalization::Hybrid) {
         return values;
     }
 
@@ -993,7 +1047,7 @@
         if (value != newvalue) values[y] = float(newvalue);
     }
 
-    if (m_normalization == ColumnOp::NormalizeHybrid
+    if (m_normalization == ColumnNormalization::Hybrid
         && (colMax > 0.0)) {
         double logmax = log10(colMax);
         for (int y = 0; y < nv; ++y) {
@@ -1062,7 +1116,7 @@
         m_cache = new QImage(cacheWidth, cacheHeight, QImage::Format_Indexed8);
         m_cache->setColorCount(256);
         m_cache->fill(0);
-        if (m_normalization != ColumnOp::NormalizeVisibleArea) {
+        if (!m_normalizeVisibleArea) {
             m_peaksCache = new QImage
                 (cacheWidth / m_peakResolution + 1, cacheHeight,
                  QImage::Format_Indexed8);
@@ -1097,7 +1151,7 @@
     if (fillEnd < 0) fillEnd = 0;
     if (fillEnd < fillStart) fillEnd = fillStart;
 
-    bool normalizeVisible = (m_normalization == ColumnOp::NormalizeVisibleArea);
+    bool normalizeVisible = (m_normalizeVisibleArea);
 
     if (!normalizeVisible && (m_cacheValidStart < m_cacheValidEnd)) {
         
@@ -1402,7 +1456,7 @@
 
     //!!!???
     
-    if (m_normalization == ColumnOp::NormalizeVisibleArea) {
+    if (m_normalizeVisibleArea) {
         rect = v->getPaintRect();
     }
 
@@ -1804,36 +1858,6 @@
     return true;
 }
 
-static ColourScale::Scale
-convertInColourScale(int fileScale)
-{
-    //!!! this is very badly handled, switching the enum directly for
-    //!!! the ColourScale one did not work out well!
-    
-    switch (fileScale) {
-    default:
-    case 0: return ColourScale::LinearColourScale;
-    case 1: return ColourScale::LogColourScale;
-    case 2: return ColourScale::PlusMinusOneScale;
-    case 3: return ColourScale::AbsoluteScale;
-    }
-}
-
-static int
-convertOutColourScale(ColourScale::Scale scale)
-{
-    switch (scale) {
-    case ColourScale::LinearColourScale: return 0;
-    case ColourScale::LogColourScale: return 1;
-    case ColourScale::PlusMinusOneScale: return 2;
-    case ColourScale::AbsoluteScale: return 3;
-
-    case ColourScale::MeterColourScale:
-    case ColourScale::PhaseColourScale:
-    default: return 0;
-    }
-}
-
 void
 Colour3DPlotLayer::toXml(QTextStream &stream,
                          QString indent, QString extraAttributes) const
@@ -1844,7 +1868,7 @@
                         "maxY=\"%4\" "
                         "invertVertical=\"%5\" "
                         "opaque=\"%6\" %7")
-	.arg(convertOutColourScale(m_colourScale))
+	.arg(convertFromColourScale(m_colourScale))
         .arg(m_colourMap)
         .arg(m_miny)
         .arg(m_maxy)
@@ -1861,18 +1885,18 @@
     // area as well afterwards
     
     s += QString("columnNormalization=\"%1\" ")
-        .arg(m_normalization == ColumnOp::NormalizeColumns ? "peak" :
-             m_normalization == ColumnOp::NormalizeHybrid ? "hybrid" : "none");
+        .arg(m_normalization == ColumnNormalization::Max1 ? "peak" :
+             m_normalization == ColumnNormalization::Hybrid ? "hybrid" : "none");
 
     // Old-style normalization attribute, for backward compatibility
     
     s += QString("normalizeColumns=\"%1\" ")
-	.arg(m_normalization == ColumnOp::NormalizeColumns ? "true" : "false");
+	.arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false");
 
     // And this applies to both old- and new-style attributes
     
     s += QString("normalizeVisibleArea=\"%1\" ")
-        .arg(m_normalization == ColumnOp::NormalizeVisibleArea ? "true" : "false");
+        .arg(m_normalizeVisibleArea ? "true" : "false");
     
     Layer::toXml(stream, indent, extraAttributes + " " + s);
 }
@@ -1882,7 +1906,7 @@
 {
     bool ok = false, alsoOk = false;
 
-    ColourScale::Scale colourScale = convertInColourScale
+    ColourScale::Scale colourScale = convertToColourScale
         (attributes.value("colourScale").toInt(&ok));
     if (ok) setColourScale(colourScale);
 
@@ -1921,11 +1945,11 @@
         haveNewStyleNormalization = true;
 
         if (columnNormalization == "peak") {
-            setNormalization(ColumnOp::NormalizeColumns);
+            setNormalization(ColumnNormalization::Max1);
         } else if (columnNormalization == "hybrid") {
-            setNormalization(ColumnOp::NormalizeHybrid);
+            setNormalization(ColumnNormalization::Hybrid);
         } else if (columnNormalization == "none") {
-            // do nothing
+            setNormalization(ColumnNormalization::None);
         } else {
             cerr << "NOTE: Unknown or unsupported columnNormalization attribute \""
                  << columnNormalization << "\"" << endl;
@@ -1934,24 +1958,24 @@
 
     if (!haveNewStyleNormalization) {
 
+        setNormalization(ColumnNormalization::None);
+
         bool normalizeColumns =
             (attributes.value("normalizeColumns").trimmed() == "true");
         if (normalizeColumns) {
-            setNormalization(ColumnOp::NormalizeColumns);
+            setNormalization(ColumnNormalization::Max1);
         }
 
         bool normalizeHybrid =
             (attributes.value("normalizeHybrid").trimmed() == "true");
         if (normalizeHybrid) {
-            setNormalization(ColumnOp::NormalizeHybrid);
+            setNormalization(ColumnNormalization::Hybrid);
         }
     }
     
     bool normalizeVisibleArea =
         (attributes.value("normalizeVisibleArea").trimmed() == "true");
-    if (normalizeVisibleArea) {
-        setNormalization(ColumnOp::NormalizeVisibleArea);
-    }
+    setNormalizeVisibleArea(normalizeVisibleArea);
 
     //!!! todo: check save/reload scaling, compare with
     //!!! SpectrogramLayer, compare with prior SV versions, compare
--- a/layer/Colour3DPlotLayer.h	Thu Jul 14 15:13:37 2016 +0100
+++ b/layer/Colour3DPlotLayer.h	Thu Jul 14 16:12:05 2016 +0100
@@ -110,10 +110,16 @@
     BinScale getBinScale() const;
 
     /**
-     * Specify the normalization mode for bin values.
+     * Specify the normalization mode for individual columns.
      */
-    void setNormalization(ColumnOp::Normalization);
-    ColumnOp::Normalization getNormalization() const;
+    void setNormalization(ColumnNormalization);
+    ColumnNormalization getNormalization() const;
+
+    /**
+     * Specify whether to normalize the visible area.
+     */
+    void setNormalizeVisibleArea(bool);
+    bool getNormalizeVisibleArea() const;
 
     void setInvertVertical(bool i);
     bool getInvertVertical() const;
@@ -158,22 +164,28 @@
     mutable int m_cacheValidEnd;
 
     ColourScale::Scale m_colourScale;
-    bool        m_colourScaleSet;
-    int         m_colourMap;
-    float       m_gain;
+    bool m_colourScaleSet;
+    int m_colourMap;
+    float m_gain;
     BinScale m_binScale;
-    ColumnOp::Normalization m_normalization;
-    bool        m_invertVertical;
-    bool        m_opaque;
-    bool        m_smooth;
-    int         m_peakResolution;
+    ColumnNormalization m_normalization; // of individual columns
+    bool m_normalizeVisibleArea;
+    bool m_invertVertical;
+    bool m_opaque;
+    bool m_smooth;
+    int m_peakResolution;
 
     // Minimum and maximum bin numbers visible within the view. We
     // always snap to whole bins at view edges.
-    int         m_miny;
-    int         m_maxy;
+    int m_miny;
+    int m_maxy;
 
-    bool        m_synchronous;
+    bool m_synchronous;
+
+    static ColourScale::Scale convertToColourScale(int value);
+    static int convertFromColourScale(ColourScale::Scale);
+    static std::pair<ColumnNormalization, bool> convertToColumnNorm(int value);
+    static int convertFromColumnNorm(ColumnNormalization norm, bool visible);
 
     mutable Dense3DModelPeakCache *m_peakCache;
     const int m_peakCacheDivisor;
--- a/layer/Colour3DPlotRenderer.h	Thu Jul 14 15:13:37 2016 +0100
+++ b/layer/Colour3DPlotRenderer.h	Thu Jul 14 16:12:05 2016 +0100
@@ -59,7 +59,7 @@
     struct Parameters {
 	Parameters() :
 	    colourScale(ColourScale::Parameters()),
-	    normalization(ColumnOp::NoNormalization),
+	    normalization(ColumnNormalization::None),
 	    binDisplay(BinDisplay::AllBins),
             binScale(BinScale::Linear),
 	    alwaysOpaque(false),
@@ -67,7 +67,7 @@
             invertVertical(false) { }
 
 	ColourScale colourScale;       // complete ColourScale object by value
-	ColumnOp::Normalization normalization;
+	ColumnNormalization normalization;
 	BinDisplay binDisplay;
 	BinScale binScale;
 	bool alwaysOpaque;
--- a/layer/SpectrogramLayer.cpp	Thu Jul 14 15:13:37 2016 +0100
+++ b/layer/SpectrogramLayer.cpp	Thu Jul 14 16:12:05 2016 +0100
@@ -76,7 +76,8 @@
     m_colourMap(0),
     m_binScale(BinScale::Linear),
     m_binDisplay(BinDisplay::AllBins),
-    m_normalization(ColumnOp::NoNormalization),
+    m_normalization(ColumnNormalization::None),
+    m_normalizeVisibleArea(false),
     m_lastEmittedZoomStep(-1),
     m_synchronous(false),
     m_haveDetailedScale(false),
@@ -112,7 +113,7 @@
 	setBinScale(BinScale::Log);
 	setColourScale(ColourScale::LinearColourScale);
 	setBinDisplay(BinDisplay::PeakFrequencies);
-        setNormalization(ColumnOp::NormalizeColumns);
+        setNormalization(ColumnNormalization::Max1);
         colourConfigName = "spectrogram-melodic-colour";
         colourConfigDefault = int(ColourMapper::Sunset);
     }
@@ -136,6 +137,60 @@
     invalidateFFTModel();
 }
 
+ColourScale::Scale
+SpectrogramLayer::convertToColourScale(int value)
+{
+    switch (value) {
+    case 0: return ColourScale::LinearColourScale;
+    case 1: return ColourScale::MeterColourScale;
+    case 2: return ColourScale::LogColourScale; //!!! db^2
+    case 3: return ColourScale::LogColourScale;
+    case 4: return ColourScale::PhaseColourScale;
+    default: return ColourScale::LinearColourScale;
+    }
+}
+
+int
+SpectrogramLayer::convertFromColourScale(ColourScale::Scale scale)
+{
+    switch (scale) {
+    case ColourScale::LinearColourScale: return 0;
+    case ColourScale::MeterColourScale: return 1;
+    case ColourScale::LogColourScale: return 3; //!!! + db^2
+    case ColourScale::PhaseColourScale: return 4;
+
+    case ColourScale::PlusMinusOneScale:
+    case ColourScale::AbsoluteScale:
+    default: return 0;
+    }
+}
+
+std::pair<ColumnNormalization, bool>
+SpectrogramLayer::convertToColumnNorm(int value)
+{
+    switch (value) {
+    default:
+    case 0: return { ColumnNormalization::None, false };
+    case 1: return { ColumnNormalization::Max1, false };
+    case 2: return { ColumnNormalization::None, true }; // visible area
+    case 3: return { ColumnNormalization::Hybrid, false };
+    }
+}
+
+int
+SpectrogramLayer::convertFromColumnNorm(ColumnNormalization norm, bool visible)
+{
+    if (visible) return 2;
+    switch (norm) {
+    case ColumnNormalization::None: return 0;
+    case ColumnNormalization::Max1: return 1;
+    case ColumnNormalization::Hybrid: return 3;
+
+    case ColumnNormalization::Sum1:
+    default: return 0;
+    }
+}
+
 void
 SpectrogramLayer::setModel(const DenseTimeValueModel *model)
 {
@@ -277,7 +332,7 @@
 	*max = 4;
         *deflt = 2;
 
-	val = (int)m_colourScale;
+	val = convertFromColourScale(m_colourScale);
 
     } else if (name == "Colour") {
 
@@ -361,8 +416,9 @@
 	
         *min = 0;
         *max = 3;
-        *deflt = int(ColumnOp::NoNormalization);
-        val = (int)m_normalization;
+        *deflt = 0;
+        
+        val = convertFromColumnNorm(m_normalization, m_normalizeVisibleArea);
 
     } else {
 	val = Layer::getPropertyRangeAndValue(name, min, max, deflt);
@@ -558,13 +614,9 @@
 	case 2: setBinDisplay(BinDisplay::PeakFrequencies); break;
 	}
     } else if (name == "Normalization") {
-        switch (value) {
-        default:
-        case 0: setNormalization(ColumnOp::NoNormalization); break;
-        case 1: setNormalization(ColumnOp::NormalizeColumns); break;
-        case 2: setNormalization(ColumnOp::NormalizeVisibleArea); break;
-        case 3: setNormalization(ColumnOp::NormalizeHybrid); break;
-        }
+        auto n = convertToColumnNorm(value);
+        setNormalization(n.first);
+        setNormalizeVisibleArea(n.second);
     }
 }
 
@@ -886,7 +938,7 @@
 }
 
 void
-SpectrogramLayer::setNormalization(ColumnOp::Normalization n)
+SpectrogramLayer::setNormalization(ColumnNormalization n)
 {
     if (m_normalization == n) return;
 
@@ -897,13 +949,31 @@
     emit layerParametersChanged();
 }
 
-ColumnOp::Normalization
+ColumnNormalization
 SpectrogramLayer::getNormalization() const
 {
     return m_normalization;
 }
 
 void
+SpectrogramLayer::setNormalizeVisibleArea(bool n)
+{
+    if (m_normalizeVisibleArea == n) return;
+
+    invalidateImageCaches();
+    invalidateMagnitudes();
+    m_normalizeVisibleArea = n;
+    
+    emit layerParametersChanged();
+}
+
+bool
+SpectrogramLayer::getNormalizeVisibleArea() const
+{
+    return m_normalizeVisibleArea;
+}
+
+void
 SpectrogramLayer::setLayerDormant(const LayerGeometryProvider *v, bool dormant)
 {
     if (dormant) {
@@ -1030,10 +1100,10 @@
     double min = 0.0;
     double max = 1.0;
 
-    if (m_normalization == ColumnOp::NormalizeVisibleArea) {
+    if (m_normalizeVisibleArea) {
         min = m_viewMags[v->getId()].getMin();
         max = m_viewMags[v->getId()].getMax();
-    } else if (m_normalization != ColumnOp::NormalizeColumns) {
+    } else if (m_normalization != ColumnNormalization::Max1) {
         if (m_colourScale == ColourScale::LinearColourScale //||
 //            m_colourScale == MeterColourScale) {
             ) {
@@ -1587,7 +1657,7 @@
         cparams.gain = m_gain;
 
         if (m_colourScale != ColourScale::PhaseColourScale &&
-            m_normalization == ColumnOp::NoNormalization) {
+            m_normalization == ColumnNormalization::None) {
             cparams.gain *= 2.f / float(getFFTSize());
         }
         
@@ -1668,7 +1738,7 @@
 #ifdef DEBUG_SPECTROGRAM_REPAINT
         cerr << "SpectrogramLayer: magnitude range changed to [" << m_viewMags[v->getId()].getMin() << "->" << m_viewMags[v->getId()].getMax() << "]" << endl;
 #endif
-        if (m_normalization == ColumnOp::NormalizeVisibleArea) {
+        if (m_normalizeVisibleArea) {
             cache.invalidate();
         }
     }
@@ -2109,7 +2179,7 @@
 
     if (!m_synchronous) {
 
-        if ((m_normalization != ColumnOp::NormalizeVisibleArea) || !overallMagChanged) {
+        if (!m_normalizeVisibleArea || !overallMagChanged) {
 
             QRect areaLeft(0, 0, cache.getValidLeft(), h);
             QRect areaRight(cache.getValidRight(), 0,
@@ -3390,33 +3460,6 @@
 
 }
 
-static ColourScale::Scale
-convertInColourScale(int fileScale)
-{
-    switch (fileScale) {
-    case 0: return ColourScale::LinearColourScale;
-    case 1: return ColourScale::MeterColourScale;
-    case 2: return ColourScale::LogColourScale; //!!!
-    case 3: return ColourScale::LogColourScale;
-    case 4: return ColourScale::PhaseColourScale;
-    default: return ColourScale::LinearColourScale;
-    }
-}
-
-static int
-convertOutColourScale(ColourScale::Scale scale)
-{
-    switch (scale) {
-    case ColourScale::LinearColourScale: return 0;
-    case ColourScale::MeterColourScale: return 1;
-    case ColourScale::LogColourScale: return 3; //!!!
-    case ColourScale::PhaseColourScale: return 4;
-    case ColourScale::PlusMinusOneScale:
-    case ColourScale::AbsoluteScale:
-    default: return 0;
-    }
-}
-
 void
 SpectrogramLayer::toXml(QTextStream &stream,
                         QString indent, QString extraAttributes) const
@@ -3443,7 +3486,7 @@
 		 "binDisplay=\"%7\" ")
 	.arg(m_minFrequency)
 	.arg(m_maxFrequency)
-	.arg(convertOutColourScale(m_colourScale))
+	.arg(convertFromColourScale(m_colourScale))
 	.arg(m_colourMap)
 	.arg(m_colourRotation)
 	.arg(int(m_binScale))
@@ -3455,8 +3498,8 @@
     // area as well afterwards
     
     s += QString("columnNormalization=\"%1\" ")
-        .arg(m_normalization == ColumnOp::NormalizeColumns ? "peak" :
-             m_normalization == ColumnOp::NormalizeHybrid ? "hybrid" : "none");
+        .arg(m_normalization == ColumnNormalization::Max1 ? "peak" :
+             m_normalization == ColumnNormalization::Hybrid ? "hybrid" : "none");
 
     // Old-style normalization attribute. We *don't* write out
     // normalizeHybrid here because the only release that would accept
@@ -3465,12 +3508,12 @@
     // v2.0+ will look odd in Tony v1.0
     
     s += QString("normalizeColumns=\"%1\" ")
-	.arg(m_normalization == ColumnOp::NormalizeColumns ? "true" : "false");
+	.arg(m_normalization == ColumnNormalization::Max1 ? "true" : "false");
 
     // And this applies to both old- and new-style attributes
     
     s += QString("normalizeVisibleArea=\"%1\" ")
-        .arg(m_normalization == ColumnOp::NormalizeVisibleArea ? "true" : "false");
+        .arg(m_normalizeVisibleArea ? "true" : "false");
     
     Layer::toXml(stream, indent, extraAttributes + " " + s);
 }
@@ -3518,7 +3561,7 @@
         setMaxFrequency(maxFrequency);
     }
 
-    ColourScale::Scale colourScale = convertInColourScale
+    ColourScale::Scale colourScale = convertToColourScale
         (attributes.value("colourScale").toInt(&ok));
     if (ok) setColourScale(colourScale);
 
@@ -3545,11 +3588,11 @@
         haveNewStyleNormalization = true;
 
         if (columnNormalization == "peak") {
-            setNormalization(ColumnOp::NormalizeColumns);
+            setNormalization(ColumnNormalization::Max1);
         } else if (columnNormalization == "hybrid") {
-            setNormalization(ColumnOp::NormalizeHybrid);
+            setNormalization(ColumnNormalization::Hybrid);
         } else if (columnNormalization == "none") {
-            // do nothing
+            setNormalization(ColumnNormalization::None);
         } else {
             cerr << "NOTE: Unknown or unsupported columnNormalization attribute \""
                  << columnNormalization << "\"" << endl;
@@ -3561,23 +3604,21 @@
         bool normalizeColumns =
             (attributes.value("normalizeColumns").trimmed() == "true");
         if (normalizeColumns) {
-            setNormalization(ColumnOp::NormalizeColumns);
+            setNormalization(ColumnNormalization::Max1);
         }
 
         bool normalizeHybrid =
             (attributes.value("normalizeHybrid").trimmed() == "true");
         if (normalizeHybrid) {
-            setNormalization(ColumnOp::NormalizeHybrid);
+            setNormalization(ColumnNormalization::Hybrid);
         }
     }
 
     bool normalizeVisibleArea =
         (attributes.value("normalizeVisibleArea").trimmed() == "true");
-    if (normalizeVisibleArea) {
-        setNormalization(ColumnOp::NormalizeVisibleArea);
-    }
-
-    if (!haveNewStyleNormalization && m_normalization == ColumnOp::NormalizeHybrid) {
+    setNormalizeVisibleArea(normalizeVisibleArea);
+
+    if (!haveNewStyleNormalization && m_normalization == ColumnNormalization::Hybrid) {
         // Tony v1.0 is (and hopefully will remain!) the only released
         // SV-a-like to use old-style attributes when saving sessions
         // that ask for hybrid normalization. It saves them with the
--- a/layer/SpectrogramLayer.h	Thu Jul 14 15:13:37 2016 +0100
+++ b/layer/SpectrogramLayer.h	Thu Jul 14 16:12:05 2016 +0100
@@ -159,10 +159,16 @@
     BinDisplay getBinDisplay() const;
 
     /**
-     * Specify the normalization mode for bin values.
+     * Specify the normalization mode for individual columns.
      */
-    void setNormalization(ColumnOp::Normalization);
-    ColumnOp::Normalization getNormalization() const;
+    void setNormalization(ColumnNormalization);
+    ColumnNormalization getNormalization() const;
+
+    /**
+     * Specify whether to normalize the visible area.
+     */
+    void setNormalizeVisibleArea(bool);
+    bool getNormalizeVisibleArea() const;
 
     /**
      * Specify the colour map. See ColourMapper for the colour map
@@ -247,14 +253,20 @@
     ColourScale::Scale  m_colourScale;
     int                 m_colourMap;
     QColor              m_crosshairColour;
-    BinScale m_binScale;
-    BinDisplay m_binDisplay;
-    ColumnOp::Normalization m_normalization;
+    BinScale            m_binScale;
+    BinDisplay          m_binDisplay;
+    ColumnNormalization m_normalization; // of individual columns
+    bool                m_normalizeVisibleArea;
     int                 m_lastEmittedZoomStep;
     bool                m_synchronous;
 
     mutable bool        m_haveDetailedScale;
 
+    static ColourScale::Scale convertToColourScale(int value);
+    static int convertFromColourScale(ColourScale::Scale);
+    static std::pair<ColumnNormalization, bool> convertToColumnNorm(int value);
+    static int convertFromColumnNorm(ColumnNormalization norm, bool visible);
+
     enum { NO_VALUE = 0 }; // colour index for unused pixels
 
     class Palette