changeset 152:21792a550ec9 last-cc-copyright

* Move the current DenseThreeDimensionalModel to EditableDenseThreeDimensionalModel (wow!), and make DTDM an abstract base * Move FFTFuzzyAdapter to FFTModel as a new subclass of DTDM
author Chris Cannam
date Mon, 31 Jul 2006 17:05:18 +0000
parents 3c1d5ef43baa
children 8d92611901d3
files base/FFTCache.cpp base/FFTCache.h base/base.pro data/data.pro data/fft/FFTCache.cpp data/fft/FFTCache.h data/fft/FFTDataServer.cpp data/fft/FFTDataServer.h data/fft/FFTFileCache.h data/fft/FFTFuzzyAdapter.cpp data/fft/FFTFuzzyAdapter.h data/fileio/CSVFileReader.cpp data/model/DenseThreeDimensionalModel.cpp data/model/DenseThreeDimensionalModel.h data/model/EditableDenseThreeDimensionalModel.cpp data/model/EditableDenseThreeDimensionalModel.h data/model/FFTModel.cpp data/model/FFTModel.h
diffstat 18 files changed, 1006 insertions(+), 830 deletions(-) [+]
line wrap: on
line diff
--- a/base/FFTCache.cpp	Mon Jul 31 16:15:45 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-#include "FFTCache.h"
-#include "system/System.h"
-
-#include <iostream>
-
-//!!! This class is a work in progress -- it does only as much as we
-// need for the current SpectrogramLayer.  Slated for substantial
-// refactoring and extension.
-
-FFTMemoryCache::FFTMemoryCache() :
-    m_width(0),
-    m_height(0),
-    m_magnitude(0),
-    m_phase(0),
-    m_factor(0)
-{
-}
-
-FFTMemoryCache::~FFTMemoryCache()
-{
-    std::cerr << "FFTMemoryCache[" << this << "]::~Cache" << std::endl;
-
-    for (size_t i = 0; i < m_width; ++i) {
-	if (m_magnitude && m_magnitude[i]) free(m_magnitude[i]);
-	if (m_phase && m_phase[i]) free(m_phase[i]);
-    }
-
-    if (m_magnitude) free(m_magnitude);
-    if (m_phase) free(m_phase);
-    if (m_factor) free(m_factor);
-}
-
-void
-FFTMemoryCache::resize(size_t width, size_t height)
-{
-    std::cerr << "FFTMemoryCache[" << this << "]::resize(" << width << "x" << height << " = " << width*height << ")" << std::endl;
-    
-    if (m_width == width && m_height == height) return;
-
-    resize(m_magnitude, width, height);
-    resize(m_phase, width, height);
-
-    m_factor = (float *)realloc(m_factor, width * sizeof(float));
-
-    m_width = width;
-    m_height = height;
-
-    std::cerr << "done, width = " << m_width << " height = " << m_height << std::endl;
-}
-
-void
-FFTMemoryCache::resize(uint16_t **&array, size_t width, size_t height)
-{
-    for (size_t i = width; i < m_width; ++i) {
-	free(array[i]);
-    }
-
-    if (width != m_width) {
-	array = (uint16_t **)realloc(array, width * sizeof(uint16_t *));
-	if (!array) throw std::bad_alloc();
-	MUNLOCK(array, width * sizeof(uint16_t *));
-    }
-
-    for (size_t i = m_width; i < width; ++i) {
-	array[i] = 0;
-    }
-
-    for (size_t i = 0; i < width; ++i) {
-	array[i] = (uint16_t *)realloc(array[i], height * sizeof(uint16_t));
-	if (!array[i]) throw std::bad_alloc();
-	MUNLOCK(array[i], height * sizeof(uint16_t));
-    }
-}
-
-void
-FFTMemoryCache::reset()
-{
-    for (size_t x = 0; x < m_width; ++x) {
-	for (size_t y = 0; y < m_height; ++y) {
-	    m_magnitude[x][y] = 0;
-	    m_phase[x][y] = 0;
-	}
-	m_factor[x] = 1.0;
-    }
-}	    
-
-void
-FFTMemoryCache::setColumnAt(size_t x, float *reals, float *imags)
-{
-    float max = 0.0;
-
-    for (size_t y = 0; y < m_height; ++y) {
-        float mag = sqrtf(reals[y] * reals[y] + imags[y] * imags[y]);
-        float phase = atan2f(imags[y], reals[y]);
-        phase = princargf(phase);
-        reals[y] = mag;
-        imags[y] = phase;
-        if (mag > max) max = mag;
-    }
-
-    setColumnAt(x, reals, imags, max);
-}
-
--- a/base/FFTCache.h	Mon Jul 31 16:15:45 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-#ifndef _FFT_CACHE_H_
-#define _FFT_CACHE_H_
-
-#include <cstdlib>
-#include <cmath>
-
-#include <stdint.h>
-
-class FFTCache
-{
-public:
-    virtual ~FFTCache() { }
-
-    virtual size_t getWidth() const = 0;
-    virtual size_t getHeight() const = 0;
-	
-    virtual void resize(size_t width, size_t height) = 0;
-    virtual void reset() = 0; // zero-fill or 1-fill as appropriate without changing size
-	
-    virtual float getMagnitudeAt(size_t x, size_t y) const = 0;
-    virtual float getNormalizedMagnitudeAt(size_t x, size_t y) const = 0;
-    virtual float getMaximumMagnitudeAt(size_t x) const = 0;
-    virtual float getPhaseAt(size_t x, size_t y) const = 0;
-
-    virtual void getValuesAt(size_t x, size_t y, float &real, float &imaginary) const = 0;
-
-    virtual bool haveSetColumnAt(size_t x) const = 0;
-
-    // may modify argument arrays
-    virtual void setColumnAt(size_t x, float *mags, float *phases, float factor) = 0;
-
-    // may modify argument arrays
-    virtual void setColumnAt(size_t x, float *reals, float *imags) = 0;
-
-    virtual void suspend() { }
-
-protected:
-    FFTCache() { }
-};
-
-
-/**
- * For the in-memory FFT cache, we would like to cache magnitude with
- * enough resolution to have gain applied afterwards and determine
- * whether something is a peak or not, and also cache phase rather
- * than only phase-adjusted frequency so that we don't have to
- * recalculate if switching between phase and magnitude displays.  At
- * the same time, we don't want to take up too much memory.  It's not
- * expected to be accurate enough to be used as input for DSP or
- * resynthesis code.
- *
- * This implies probably 16 bits for a normalized magnitude and at
- * most 16 bits for phase.
- *
- * Each column's magnitudes are expected to be stored normalized
- * to [0,1] with respect to the column, so the normalization
- * factor should be calculated before all values in a column, and
- * set appropriately.
- */
-
-class FFTMemoryCache : public FFTCache
-{
-public:
-    FFTMemoryCache(); // of size zero, call resize() before using
-    virtual ~FFTMemoryCache();
-	
-    virtual size_t getWidth() const { return m_width; }
-    virtual size_t getHeight() const { return m_height; }
-	
-    virtual void resize(size_t width, size_t height);
-    virtual void reset(); // zero-fill or 1-fill as appropriate without changing size
-    
-    virtual float getMagnitudeAt(size_t x, size_t y) const {
-        return getNormalizedMagnitudeAt(x, y) * m_factor[x];
-    }
-    
-    virtual float getNormalizedMagnitudeAt(size_t x, size_t y) const {
-        return float(m_magnitude[x][y]) / 65535.0;
-    }
-    
-    virtual float getMaximumMagnitudeAt(size_t x) const {
-        return m_factor[x];
-    }
-    
-    virtual float getPhaseAt(size_t x, size_t y) const {
-        int16_t i = (int16_t)m_phase[x][y];
-        return (float(i) / 32767.0) * M_PI;
-    }
-    
-    virtual void getValuesAt(size_t x, size_t y, float &real, float &imag) const {
-        float mag = getMagnitudeAt(x, y);
-        float phase = getPhaseAt(x, y);
-        real = mag * cosf(phase);
-        imag = mag * sinf(phase);
-    }
-
-    virtual void setNormalizationFactor(size_t x, float factor) {
-        if (x < m_width) m_factor[x] = factor;
-    }
-    
-    virtual void setMagnitudeAt(size_t x, size_t y, float mag) {
-         // norm factor must already be set
-        setNormalizedMagnitudeAt(x, y, mag / m_factor[x]);
-    }
-    
-    virtual void setNormalizedMagnitudeAt(size_t x, size_t y, float norm) {
-        if (x < m_width && y < m_height) {
-            m_magnitude[x][y] = uint16_t(norm * 65535.0);
-        }
-    }
-    
-    virtual void setPhaseAt(size_t x, size_t y, float phase) {
-        // phase in range -pi -> pi
-        if (x < m_width && y < m_height) {
-            m_phase[x][y] = uint16_t(int16_t((phase * 32767) / M_PI));
-        }
-    }
-    
-    virtual bool haveSetColumnAt(size_t) const {
-        return true;
-    }
-
-    virtual void setColumnAt(size_t x, float *mags, float *phases, float factor) {
-        setNormalizationFactor(x, factor);
-        for (size_t y = 0; y < m_height; ++y) {
-            setMagnitudeAt(x, y, mags[y]);
-            setPhaseAt(x, y, phases[y]);
-        }
-    }
-
-    virtual void setColumnAt(size_t x, float *reals, float *imags);
-
-private:
-    size_t m_width;
-    size_t m_height;
-    uint16_t **m_magnitude;
-    uint16_t **m_phase;
-    float *m_factor;
-
-    void resize(uint16_t **&, size_t, size_t);
-};
-
-#endif
--- a/base/base.pro	Mon Jul 31 16:15:45 2006 +0000
+++ b/base/base.pro	Mon Jul 31 17:05:18 2006 +0000
@@ -20,7 +20,6 @@
            CommandHistory.h \
            ConfigFile.h \
            Exceptions.h \
-           FFTCache.h \
            Pitch.h \
            PlayParameterRepository.h \
            PlayParameters.h \
@@ -45,7 +44,6 @@
            CommandHistory.cpp \
            ConfigFile.cpp \
            Exceptions.cpp \
-           FFTCache.cpp \
            Pitch.cpp \
            PlayParameterRepository.cpp \
            PlayParameters.cpp \
--- a/data/data.pro	Mon Jul 31 16:15:45 2006 +0000
+++ b/data/data.pro	Mon Jul 31 17:05:18 2006 +0000
@@ -13,9 +13,9 @@
 MOC_DIR = tmp_moc
 
 # Input
-HEADERS += fft/FFTDataServer.h \
+HEADERS += fft/FFTCache.h \
+           fft/FFTDataServer.h \
            fft/FFTFileCache.h \
-           fft/FFTFuzzyAdapter.h \
            fileio/AudioFileReader.h \
            fileio/AudioFileReaderFactory.h \
            fileio/BZipFileDevice.h \
@@ -33,6 +33,8 @@
            fileio/WavFileWriter.h \
            model/DenseThreeDimensionalModel.h \
            model/DenseTimeValueModel.h \
+           model/EditableDenseThreeDimensionalModel.h \
+           model/FFTModel.h \
            model/Model.h \
            model/NoteModel.h \
            model/PowerOfSqrtTwoZoomConstraint.h \
@@ -44,9 +46,9 @@
            model/SparseValueModel.h \
            model/TextModel.h \
            model/WaveFileModel.h
-SOURCES += fft/FFTDataServer.cpp \
+SOURCES += fft/FFTCache.cpp \
+           fft/FFTDataServer.cpp \
            fft/FFTFileCache.cpp \
-           fft/FFTFuzzyAdapter.cpp \
            fileio/AudioFileReaderFactory.cpp \
            fileio/BZipFileDevice.cpp \
            fileio/CodedAudioFileReader.cpp \
@@ -60,8 +62,9 @@
            fileio/OggVorbisFileReader.cpp \
            fileio/WavFileReader.cpp \
            fileio/WavFileWriter.cpp \
-           model/DenseThreeDimensionalModel.cpp \
            model/DenseTimeValueModel.cpp \
+           model/EditableDenseThreeDimensionalModel.cpp \
+           model/FFTModel.cpp \
            model/Model.cpp \
            model/NoteModel.cpp \
            model/PowerOfSqrtTwoZoomConstraint.cpp \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fft/FFTCache.cpp	Mon Jul 31 17:05:18 2006 +0000
@@ -0,0 +1,118 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "FFTCache.h"
+#include "system/System.h"
+
+#include <iostream>
+
+//!!! This class is a work in progress -- it does only as much as we
+// need for the current SpectrogramLayer.  Slated for substantial
+// refactoring and extension.
+
+FFTMemoryCache::FFTMemoryCache() :
+    m_width(0),
+    m_height(0),
+    m_magnitude(0),
+    m_phase(0),
+    m_factor(0)
+{
+}
+
+FFTMemoryCache::~FFTMemoryCache()
+{
+    std::cerr << "FFTMemoryCache[" << this << "]::~Cache" << std::endl;
+
+    for (size_t i = 0; i < m_width; ++i) {
+	if (m_magnitude && m_magnitude[i]) free(m_magnitude[i]);
+	if (m_phase && m_phase[i]) free(m_phase[i]);
+    }
+
+    if (m_magnitude) free(m_magnitude);
+    if (m_phase) free(m_phase);
+    if (m_factor) free(m_factor);
+}
+
+void
+FFTMemoryCache::resize(size_t width, size_t height)
+{
+    std::cerr << "FFTMemoryCache[" << this << "]::resize(" << width << "x" << height << " = " << width*height << ")" << std::endl;
+    
+    if (m_width == width && m_height == height) return;
+
+    resize(m_magnitude, width, height);
+    resize(m_phase, width, height);
+
+    m_factor = (float *)realloc(m_factor, width * sizeof(float));
+
+    m_width = width;
+    m_height = height;
+
+    std::cerr << "done, width = " << m_width << " height = " << m_height << std::endl;
+}
+
+void
+FFTMemoryCache::resize(uint16_t **&array, size_t width, size_t height)
+{
+    for (size_t i = width; i < m_width; ++i) {
+	free(array[i]);
+    }
+
+    if (width != m_width) {
+	array = (uint16_t **)realloc(array, width * sizeof(uint16_t *));
+	if (!array) throw std::bad_alloc();
+	MUNLOCK(array, width * sizeof(uint16_t *));
+    }
+
+    for (size_t i = m_width; i < width; ++i) {
+	array[i] = 0;
+    }
+
+    for (size_t i = 0; i < width; ++i) {
+	array[i] = (uint16_t *)realloc(array[i], height * sizeof(uint16_t));
+	if (!array[i]) throw std::bad_alloc();
+	MUNLOCK(array[i], height * sizeof(uint16_t));
+    }
+}
+
+void
+FFTMemoryCache::reset()
+{
+    for (size_t x = 0; x < m_width; ++x) {
+	for (size_t y = 0; y < m_height; ++y) {
+	    m_magnitude[x][y] = 0;
+	    m_phase[x][y] = 0;
+	}
+	m_factor[x] = 1.0;
+    }
+}	    
+
+void
+FFTMemoryCache::setColumnAt(size_t x, float *reals, float *imags)
+{
+    float max = 0.0;
+
+    for (size_t y = 0; y < m_height; ++y) {
+        float mag = sqrtf(reals[y] * reals[y] + imags[y] * imags[y]);
+        float phase = atan2f(imags[y], reals[y]);
+        phase = princargf(phase);
+        reals[y] = mag;
+        imags[y] = phase;
+        if (mag > max) max = mag;
+    }
+
+    setColumnAt(x, reals, imags, max);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/fft/FFTCache.h	Mon Jul 31 17:05:18 2006 +0000
@@ -0,0 +1,158 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef _FFT_CACHE_H_
+#define _FFT_CACHE_H_
+
+#include <cstdlib>
+#include <cmath>
+
+#include <stdint.h>
+
+class FFTCache
+{
+public:
+    virtual ~FFTCache() { }
+
+    virtual size_t getWidth() const = 0;
+    virtual size_t getHeight() const = 0;
+	
+    virtual void resize(size_t width, size_t height) = 0;
+    virtual void reset() = 0; // zero-fill or 1-fill as appropriate without changing size
+	
+    virtual float getMagnitudeAt(size_t x, size_t y) const = 0;
+    virtual float getNormalizedMagnitudeAt(size_t x, size_t y) const = 0;
+    virtual float getMaximumMagnitudeAt(size_t x) const = 0;
+    virtual float getPhaseAt(size_t x, size_t y) const = 0;
+
+    virtual void getValuesAt(size_t x, size_t y, float &real, float &imaginary) const = 0;
+
+    virtual bool haveSetColumnAt(size_t x) const = 0;
+
+    // may modify argument arrays
+    virtual void setColumnAt(size_t x, float *mags, float *phases, float factor) = 0;
+
+    // may modify argument arrays
+    virtual void setColumnAt(size_t x, float *reals, float *imags) = 0;
+
+    virtual void suspend() { }
+
+protected:
+    FFTCache() { }
+};
+
+
+/**
+ * For the in-memory FFT cache, we would like to cache magnitude with
+ * enough resolution to have gain applied afterwards and determine
+ * whether something is a peak or not, and also cache phase rather
+ * than only phase-adjusted frequency so that we don't have to
+ * recalculate if switching between phase and magnitude displays.  At
+ * the same time, we don't want to take up too much memory.  It's not
+ * expected to be accurate enough to be used as input for DSP or
+ * resynthesis code.
+ *
+ * This implies probably 16 bits for a normalized magnitude and at
+ * most 16 bits for phase.
+ *
+ * Each column's magnitudes are expected to be stored normalized
+ * to [0,1] with respect to the column, so the normalization
+ * factor should be calculated before all values in a column, and
+ * set appropriately.
+ */
+
+class FFTMemoryCache : public FFTCache
+{
+public:
+    FFTMemoryCache(); // of size zero, call resize() before using
+    virtual ~FFTMemoryCache();
+	
+    virtual size_t getWidth() const { return m_width; }
+    virtual size_t getHeight() const { return m_height; }
+	
+    virtual void resize(size_t width, size_t height);
+    virtual void reset(); // zero-fill or 1-fill as appropriate without changing size
+    
+    virtual float getMagnitudeAt(size_t x, size_t y) const {
+        return getNormalizedMagnitudeAt(x, y) * m_factor[x];
+    }
+    
+    virtual float getNormalizedMagnitudeAt(size_t x, size_t y) const {
+        return float(m_magnitude[x][y]) / 65535.0;
+    }
+    
+    virtual float getMaximumMagnitudeAt(size_t x) const {
+        return m_factor[x];
+    }
+    
+    virtual float getPhaseAt(size_t x, size_t y) const {
+        int16_t i = (int16_t)m_phase[x][y];
+        return (float(i) / 32767.0) * M_PI;
+    }
+    
+    virtual void getValuesAt(size_t x, size_t y, float &real, float &imag) const {
+        float mag = getMagnitudeAt(x, y);
+        float phase = getPhaseAt(x, y);
+        real = mag * cosf(phase);
+        imag = mag * sinf(phase);
+    }
+
+    virtual void setNormalizationFactor(size_t x, float factor) {
+        if (x < m_width) m_factor[x] = factor;
+    }
+    
+    virtual void setMagnitudeAt(size_t x, size_t y, float mag) {
+         // norm factor must already be set
+        setNormalizedMagnitudeAt(x, y, mag / m_factor[x]);
+    }
+    
+    virtual void setNormalizedMagnitudeAt(size_t x, size_t y, float norm) {
+        if (x < m_width && y < m_height) {
+            m_magnitude[x][y] = uint16_t(norm * 65535.0);
+        }
+    }
+    
+    virtual void setPhaseAt(size_t x, size_t y, float phase) {
+        // phase in range -pi -> pi
+        if (x < m_width && y < m_height) {
+            m_phase[x][y] = uint16_t(int16_t((phase * 32767) / M_PI));
+        }
+    }
+    
+    virtual bool haveSetColumnAt(size_t) const {
+        return true;
+    }
+
+    virtual void setColumnAt(size_t x, float *mags, float *phases, float factor) {
+        setNormalizationFactor(x, factor);
+        for (size_t y = 0; y < m_height; ++y) {
+            setMagnitudeAt(x, y, mags[y]);
+            setPhaseAt(x, y, phases[y]);
+        }
+    }
+
+    virtual void setColumnAt(size_t x, float *reals, float *imags);
+
+private:
+    size_t m_width;
+    size_t m_height;
+    uint16_t **m_magnitude;
+    uint16_t **m_phase;
+    float *m_factor;
+
+    void resize(uint16_t **&, size_t, size_t);
+};
+
+#endif
--- a/data/fft/FFTDataServer.cpp	Mon Jul 31 16:15:45 2006 +0000
+++ b/data/fft/FFTDataServer.cpp	Mon Jul 31 17:05:18 2006 +0000
@@ -112,11 +112,11 @@
     // smaller increment, we can draw the results from it (provided
     // our increment is a multiple of its)
     //
-    // The FFTFuzzyAdapter knows how to interpret these things.  In
+    // The FFTModel knows how to interpret these things.  In
     // both cases we require that the larger one is a power-of-two
     // multiple of the smaller (e.g. even though in principle you can
     // draw the results at increment 256 from those at increment 768
-    // or 1536, the fuzzy adapter doesn't support this).
+    // or 1536, the model doesn't support this).
 
     {
         QMutexLocker locker(&m_serverMapMutex);
@@ -208,6 +208,23 @@
 }
 
 void
+FFTDataServer::claimInstance(FFTDataServer *server)
+{
+    
+    QMutexLocker locker(&m_serverMapMutex);
+
+    for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
+        if (i->second.first == server) {
+            ++i->second.second;
+            return;
+        }
+    }
+    
+    std::cerr << "ERROR: FFTDataServer::claimInstance: instance "
+              << server << " unknown!" << std::endl;
+}
+
+void
 FFTDataServer::releaseInstance(FFTDataServer *server)
 {
 #ifdef DEBUG_FFT_SERVER
--- a/data/fft/FFTDataServer.h	Mon Jul 31 16:15:45 2006 +0000
+++ b/data/fft/FFTDataServer.h	Mon Jul 31 17:05:18 2006 +0000
@@ -52,6 +52,7 @@
                                            bool polar,
                                            size_t fillFromColumn = 0);
 
+    static void claimInstance(FFTDataServer *);
     static void releaseInstance(FFTDataServer *);
 
     const DenseTimeValueModel *getModel() const { return m_model; }
--- a/data/fft/FFTFileCache.h	Mon Jul 31 16:15:45 2006 +0000
+++ b/data/fft/FFTFileCache.h	Mon Jul 31 17:05:18 2006 +0000
@@ -16,7 +16,7 @@
 #ifndef _FFT_FILE_CACHE_H_
 #define _FFT_FILE_CACHE_H_
 
-#include "base/FFTCache.h"
+#include "FFTCache.h"
 #include "fileio/MatrixFile.h"
 
 #include <QMutex>
--- a/data/fft/FFTFuzzyAdapter.cpp	Mon Jul 31 16:15:45 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-#include "FFTFuzzyAdapter.h"
-
-#include <cassert>
-
-FFTFuzzyAdapter::FFTFuzzyAdapter(const DenseTimeValueModel *model,
-				 int channel,
-				 WindowType windowType,
-				 size_t windowSize,
-				 size_t windowIncrement,
-				 size_t fftSize,
-				 bool polar,
-				 size_t fillFromColumn) :
-    m_server(0),
-    m_xshift(0),
-    m_yshift(0)
-{
-    m_server = FFTDataServer::getFuzzyInstance(model,
-                                               channel,
-                                               windowType,
-                                               windowSize,
-                                               windowIncrement,
-                                               fftSize,
-                                               polar,
-                                               fillFromColumn);
-
-    size_t xratio = windowIncrement / m_server->getWindowIncrement();
-    size_t yratio = m_server->getFFTSize() / fftSize;
-
-    while (xratio > 1) {
-        if (xratio & 0x1) {
-            std::cerr << "ERROR: FFTFuzzyAdapter: Window increment ratio "
-                      << windowIncrement << " / "
-                      << m_server->getWindowIncrement()
-                      << " must be a power of two" << std::endl;
-            assert(!(xratio & 0x1));
-        }
-        ++m_xshift;
-        xratio >>= 1;
-    }
-
-    while (yratio > 1) {
-        if (yratio & 0x1) {
-            std::cerr << "ERROR: FFTFuzzyAdapter: FFT size ratio "
-                      << m_server->getFFTSize() << " / " << fftSize
-                      << " must be a power of two" << std::endl;
-            assert(!(yratio & 0x1));
-        }
-        ++m_yshift;
-        yratio >>= 1;
-    }
-}
-
-FFTFuzzyAdapter::~FFTFuzzyAdapter()
-{
-    FFTDataServer::releaseInstance(m_server);
-}
-
--- a/data/fft/FFTFuzzyAdapter.h	Mon Jul 31 16:15:45 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,80 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-#ifndef _FFT_FUZZY_ADAPTER_H_
-#define _FFT_FUZZY_ADAPTER_H_
-
-#include "FFTDataServer.h"
-
-class FFTFuzzyAdapter
-{
-public:
-    FFTFuzzyAdapter(const DenseTimeValueModel *model,
-                    int channel,
-                    WindowType windowType,
-                    size_t windowSize,
-                    size_t windowIncrement,
-                    size_t fftSize,
-                    bool polar,
-                    size_t fillFromColumn = 0);
-    ~FFTFuzzyAdapter();
-
-    size_t getWidth() const {
-        return m_server->getWidth() >> m_xshift;
-    }
-    size_t getHeight() const {
-        return m_server->getHeight() >> m_yshift;
-    }
-    float getMagnitudeAt(size_t x, size_t y) {
-        return m_server->getMagnitudeAt(x << m_xshift, y << m_yshift);
-    }
-    float getNormalizedMagnitudeAt(size_t x, size_t y) {
-        return m_server->getNormalizedMagnitudeAt(x << m_xshift, y << m_yshift);
-    }
-    float getMaximumMagnitudeAt(size_t x) {
-        return m_server->getMaximumMagnitudeAt(x << m_xshift);
-    }
-    float getPhaseAt(size_t x, size_t y) {
-        return m_server->getPhaseAt(x << m_xshift, y << m_yshift);
-    }
-    void getValuesAt(size_t x, size_t y, float &real, float &imaginary) {
-        m_server->getValuesAt(x << m_xshift, y << m_yshift, real, imaginary);
-    }
-    bool isColumnReady(size_t x) {
-        return m_server->isColumnReady(x << m_xshift);
-    }
-    bool isLocalPeak(size_t x, size_t y) {
-        float mag = getMagnitudeAt(x, y);
-        if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false;
-        if (y < getHeight() - 1 && mag < getMagnitudeAt(x, y + 1)) return false;
-        return true;
-    }
-    bool isOverThreshold(size_t x, size_t y, float threshold) {
-        return getMagnitudeAt(x, y) > threshold;
-    }
-
-    size_t getFillCompletion() const { return m_server->getFillCompletion(); }
-    size_t getFillExtent() const { return m_server->getFillExtent(); }
-
-private:
-    FFTFuzzyAdapter(const FFTFuzzyAdapter &); // not implemented
-    FFTFuzzyAdapter &operator=(const FFTFuzzyAdapter &); // not implemented
-
-    FFTDataServer *m_server;
-    int m_xshift;
-    int m_yshift;
-};
-
-#endif
--- a/data/fileio/CSVFileReader.cpp	Mon Jul 31 16:15:45 2006 +0000
+++ b/data/fileio/CSVFileReader.cpp	Mon Jul 31 17:05:18 2006 +0000
@@ -19,7 +19,7 @@
 #include "base/RealTime.h"
 #include "model/SparseOneDimensionalModel.h"
 #include "model/SparseTimeValueModel.h"
-#include "model/DenseThreeDimensionalModel.h"
+#include "model/EditableDenseThreeDimensionalModel.h"
 
 #include <QFile>
 #include <QString>
@@ -112,7 +112,7 @@
 
     SparseOneDimensionalModel *model1 = 0;
     SparseTimeValueModel *model2 = 0;
-    DenseThreeDimensionalModel *model3 = 0;
+    EditableDenseThreeDimensionalModel *model3 = 0;
     Model *model = 0;
 
     QTextStream in(m_file);
@@ -149,8 +149,9 @@
 		break;
 		
 	    case CSVFormatDialog::ThreeDimensionalModel:
-		model3 = new DenseThreeDimensionalModel(sampleRate, windowSize,
-							list.size());
+		model3 = new EditableDenseThreeDimensionalModel(sampleRate,
+                                                                windowSize,
+                                                                list.size());
 		model = model3;
 		break;
 	    }
--- a/data/model/DenseThreeDimensionalModel.cpp	Mon Jul 31 16:15:45 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,314 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-#include "DenseThreeDimensionalModel.h"
-
-#include <QTextStream>
-
-DenseThreeDimensionalModel::DenseThreeDimensionalModel(size_t sampleRate,
-						       size_t windowSize,
-						       size_t yBinCount,
-						       bool notifyOnAdd) :
-    m_sampleRate(sampleRate),
-    m_windowSize(windowSize),
-    m_yBinCount(yBinCount),
-    m_minimum(0.0),
-    m_maximum(0.0),
-    m_notifyOnAdd(notifyOnAdd),
-    m_sinceLastNotifyMin(-1),
-    m_sinceLastNotifyMax(-1),
-    m_completion(100)
-{
-}    
-
-bool
-DenseThreeDimensionalModel::isOK() const
-{
-    return true;
-}
-
-size_t
-DenseThreeDimensionalModel::getSampleRate() const
-{
-    return m_sampleRate;
-}
-
-size_t
-DenseThreeDimensionalModel::getStartFrame() const
-{
-    return 0;
-}
-
-size_t
-DenseThreeDimensionalModel::getEndFrame() const
-{
-    return m_windowSize * m_data.size() + (m_windowSize - 1);
-}
-
-Model *
-DenseThreeDimensionalModel::clone() const
-{
-    DenseThreeDimensionalModel *model = new DenseThreeDimensionalModel
-	(m_sampleRate, m_windowSize, m_yBinCount);
-
-    model->m_minimum = m_minimum;
-    model->m_maximum = m_maximum;
-
-    for (size_t i = 0; i < m_data.size(); ++i) {
-	model->setBinValues(i * m_windowSize, m_data[i]);
-    }
-
-    return model;
-}
-
-size_t
-DenseThreeDimensionalModel::getWindowSize() const
-{
-    return m_windowSize;
-}
-
-void
-DenseThreeDimensionalModel::setWindowSize(size_t sz)
-{
-    m_windowSize = sz;
-}
-
-size_t
-DenseThreeDimensionalModel::getYBinCount() const
-{
-    return m_yBinCount;
-}
-
-void
-DenseThreeDimensionalModel::setYBinCount(size_t sz)
-{
-    m_yBinCount = sz;
-}
-
-float
-DenseThreeDimensionalModel::getMinimumLevel() const
-{
-    return m_minimum;
-}
-
-void
-DenseThreeDimensionalModel::setMinimumLevel(float level)
-{
-    m_minimum = level;
-}
-
-float
-DenseThreeDimensionalModel::getMaximumLevel() const
-{
-    return m_maximum;
-}
-
-void
-DenseThreeDimensionalModel::setMaximumLevel(float level)
-{
-    m_maximum = level;
-}
-
-void
-DenseThreeDimensionalModel::getBinValues(long windowStart,
-					 BinValueSet &result) const
-{
-    QMutexLocker locker(&m_mutex);
-    
-    long index = windowStart / m_windowSize;
-
-    if (index >= 0 && index < long(m_data.size())) {
-	result = m_data[index];
-    } else {
-	result.clear();
-    }
-
-    while (result.size() < m_yBinCount) result.push_back(m_minimum);
-}
-
-float
-DenseThreeDimensionalModel::getBinValue(long windowStart,
-					size_t n) const
-{
-    QMutexLocker locker(&m_mutex);
-    
-    long index = windowStart / m_windowSize;
-
-    if (index >= 0 && index < long(m_data.size())) {
-	const BinValueSet &s = m_data[index];
-	if (n < s.size()) return s[n];
-    }
-
-    return m_minimum;
-}
-
-void
-DenseThreeDimensionalModel::setBinValues(long windowStart,
-					 const BinValueSet &values)
-{
-    QMutexLocker locker(&m_mutex);
-
-    long index = windowStart / m_windowSize;
-
-    while (index >= long(m_data.size())) {
-	m_data.push_back(BinValueSet());
-    }
-
-    bool newExtents = (m_data.empty() && (m_minimum == m_maximum));
-    bool allChange = false;
-
-    for (size_t i = 0; i < values.size(); ++i) {
-	if (newExtents || values[i] < m_minimum) {
-	    m_minimum = values[i];
-	    allChange = true;
-	}
-	if (newExtents || values[i] > m_maximum) {
-	    m_maximum = values[i];
-	    allChange = true;
-	}
-    }
-
-    m_data[index] = values;
-
-    if (m_notifyOnAdd) {
-	if (allChange) {
-	    emit modelChanged();
-	} else {
-	    emit modelChanged(windowStart, windowStart + m_windowSize);
-	}
-    } else {
-	if (allChange) {
-	    m_sinceLastNotifyMin = -1;
-	    m_sinceLastNotifyMax = -1;
-	    emit modelChanged();
-	} else {
-	    if (m_sinceLastNotifyMin == -1 ||
-		windowStart < m_sinceLastNotifyMin) {
-		m_sinceLastNotifyMin = windowStart;
-	    }
-	    if (m_sinceLastNotifyMax == -1 ||
-		windowStart > m_sinceLastNotifyMax) {
-		m_sinceLastNotifyMax = windowStart;
-	    }
-	}
-    }
-}
-
-QString
-DenseThreeDimensionalModel::getBinName(size_t n) const
-{
-    if (m_binNames.size() > n) return m_binNames[n];
-    else return "";
-}
-
-void
-DenseThreeDimensionalModel::setBinName(size_t n, QString name)
-{
-    while (m_binNames.size() <= n) m_binNames.push_back("");
-    m_binNames[n] = name;
-    emit modelChanged();
-}
-
-void
-DenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
-{
-    m_binNames = names;
-    emit modelChanged();
-}
-
-void
-DenseThreeDimensionalModel::setCompletion(int completion)
-{
-    if (m_completion != completion) {
-	m_completion = completion;
-
-	if (completion == 100) {
-
-	    m_notifyOnAdd = true; // henceforth
-	    emit modelChanged();
-
-	} else if (!m_notifyOnAdd) {
-
-	    if (m_sinceLastNotifyMin >= 0 &&
-		m_sinceLastNotifyMax >= 0) {
-		emit modelChanged(m_sinceLastNotifyMin,
-				  m_sinceLastNotifyMax + m_windowSize);
-		m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
-	    } else {
-		emit completionChanged();
-	    }
-	} else {
-	    emit completionChanged();
-	}	    
-    }
-}
-
-void
-DenseThreeDimensionalModel::toXml(QTextStream &out,
-                                  QString indent,
-                                  QString extraAttributes) const
-{
-    out << Model::toXmlString
-	(indent, QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" %6")
-	 .arg(m_windowSize)
-	 .arg(m_yBinCount)
-	 .arg(m_minimum)
-	 .arg(m_maximum)
-	 .arg(getObjectExportId(&m_data))
-	 .arg(extraAttributes));
-
-    out << indent;
-    out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
-	.arg(getObjectExportId(&m_data));
-
-    for (size_t i = 0; i < m_binNames.size(); ++i) {
-	if (m_binNames[i] != "") {
-	    out << indent + "  ";
-	    out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
-		.arg(i).arg(m_binNames[i]);
-	}
-    }
-
-    for (size_t i = 0; i < m_data.size(); ++i) {
-	out << indent + "  ";
-	out << QString("<row n=\"%1\">").arg(i);
-	for (size_t j = 0; j < m_data[i].size(); ++j) {
-	    if (j > 0) out << " ";
-	    out << m_data[i][j];
-	}
-	out << QString("</row>\n");
-    }
-
-    out << indent + "</dataset>\n";
-}
-
-QString
-DenseThreeDimensionalModel::toXmlString(QString indent,
-					QString extraAttributes) const
-{
-    QString s;
-
-    {
-        QTextStream out(&s);
-        toXml(out, indent, extraAttributes);
-    }
-
-    return s;
-}
-
-#ifdef INCLUDE_MOCFILES
-#include "DenseThreeDimensionalModel.moc.cpp"
-#endif
-
--- a/data/model/DenseThreeDimensionalModel.h	Mon Jul 31 16:15:45 2006 +0000
+++ b/data/model/DenseThreeDimensionalModel.h	Mon Jul 31 17:05:18 2006 +0000
@@ -29,60 +29,25 @@
     Q_OBJECT
 
 public:
-    //!!! need to reconcile terminology -- windowSize here, resolution in sparse models
-    DenseThreeDimensionalModel(size_t sampleRate,
-			       size_t windowSize,
-			       size_t yBinCount,
-			       bool notifyOnAdd = true);
-
-    virtual bool isOK() const;
-
-    virtual size_t getSampleRate() const;
-    virtual size_t getStartFrame() const;
-    virtual size_t getEndFrame() const;
-
-    virtual Model *clone() const;
-    
-
     /**
      * Return the number of sample frames covered by each set of bins.
      */
-    virtual size_t getWindowSize() const;
-
-    /**
-     * Set the number of sample frames covered by each set of bins.
-     */
-    virtual void setWindowSize(size_t sz);
+    virtual size_t getResolution() const = 0;
 
     /**
      * Return the number of bins in each set of bins.
      */
-    virtual size_t getYBinCount() const; 
-
-    /**
-     * Set the number of bins in each set of bins.
-     */
-    virtual void setYBinCount(size_t sz);
+    virtual size_t getYBinCount() const = 0; 
 
     /**
      * Return the minimum value of the value in each bin.
      */
-    virtual float getMinimumLevel() const;
-
-    /**
-     * Set the minimum value of the value in a bin.
-     */
-    virtual void setMinimumLevel(float sz);
+    virtual float getMinimumLevel() const = 0;
 
     /**
      * Return the maximum value of the value in each bin.
      */
-    virtual float getMaximumLevel() const;
-
-    /**
-     * Set the maximum value of the value in a bin.
-     */
-    virtual void setMaximumLevel(float sz);
+    virtual float getMaximumLevel() const = 0;
 
     typedef std::vector<float> BinValueSet;
 
@@ -90,50 +55,20 @@
      * Get the set of bin values at the given sample frame (i.e. the
      * windowStartFrame/getWindowSize()'th set of bins).
      */
-    virtual void getBinValues(long windowStartFrame, BinValueSet &result) const;
+    virtual void getBinValues(long windowStartFrame, BinValueSet &result) const = 0;
 
     /**
      * Get a single value, the one at the n'th bin of the set of bins
      * starting at the given sample frame.
      */
-    virtual float getBinValue(long windowStartFrame, size_t n) const;
+    virtual float getBinValue(long windowStartFrame, size_t n) const = 0;
 
-    /**
-     * Set the entire set of bin values at the given sample frame.
-     */
-    virtual void setBinValues(long windowStartFrame, const BinValueSet &values);
+    virtual QString getBinName(size_t n) const = 0;
 
-    virtual QString getBinName(size_t n) const;
-    virtual void setBinName(size_t n, QString);
-    virtual void setBinNames(std::vector<QString> names);
-
-    virtual void setCompletion(int completion);
-    virtual int getCompletion() const { return m_completion; }
-
-    virtual void toXml(QTextStream &out,
-                       QString indent = "",
-                       QString extraAttributes = "") const;
-
-    virtual QString toXmlString(QString indent = "",
-				QString extraAttributes = "") const;
+    virtual int getCompletion() const = 0;
 
 protected:
-    typedef std::vector<BinValueSet> ValueMatrix;
-    ValueMatrix m_data;
-
-    std::vector<QString> m_binNames;
-
-    size_t m_sampleRate;
-    size_t m_windowSize;
-    size_t m_yBinCount;
-    float m_minimum;
-    float m_maximum;
-    bool m_notifyOnAdd;
-    long m_sinceLastNotifyMin;
-    long m_sinceLastNotifyMax;
-    int m_completion;
-
-    mutable QMutex m_mutex;
+    DenseThreeDimensionalModel() { }
 };
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/EditableDenseThreeDimensionalModel.cpp	Mon Jul 31 17:05:18 2006 +0000
@@ -0,0 +1,317 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "EditableDenseThreeDimensionalModel.h"
+
+#include <QTextStream>
+
+EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(size_t sampleRate,
+                                                                       size_t resolution,
+                                                                       size_t yBinCount,
+                                                                       bool notifyOnAdd) :
+    m_sampleRate(sampleRate),
+    m_resolution(resolution),
+    m_yBinCount(yBinCount),
+    m_minimum(0.0),
+    m_maximum(0.0),
+    m_notifyOnAdd(notifyOnAdd),
+    m_sinceLastNotifyMin(-1),
+    m_sinceLastNotifyMax(-1),
+    m_completion(100)
+{
+}    
+
+bool
+EditableDenseThreeDimensionalModel::isOK() const
+{
+    return true;
+}
+
+size_t
+EditableDenseThreeDimensionalModel::getSampleRate() const
+{
+    return m_sampleRate;
+}
+
+size_t
+EditableDenseThreeDimensionalModel::getStartFrame() const
+{
+    return 0;
+}
+
+size_t
+EditableDenseThreeDimensionalModel::getEndFrame() const
+{
+    return m_resolution * m_data.size() + (m_resolution - 1);
+}
+
+Model *
+EditableDenseThreeDimensionalModel::clone() const
+{
+    EditableDenseThreeDimensionalModel *model =
+        new EditableDenseThreeDimensionalModel
+	(m_sampleRate, m_resolution, m_yBinCount);
+
+    model->m_minimum = m_minimum;
+    model->m_maximum = m_maximum;
+
+    for (size_t i = 0; i < m_data.size(); ++i) {
+	model->setBinValues(i * m_resolution, m_data[i]);
+    }
+
+    return model;
+}
+
+size_t
+EditableDenseThreeDimensionalModel::getResolution() const
+{
+    return m_resolution;
+}
+
+void
+EditableDenseThreeDimensionalModel::setResolution(size_t sz)
+{
+    m_resolution = sz;
+}
+
+size_t
+EditableDenseThreeDimensionalModel::getYBinCount() const
+{
+    return m_yBinCount;
+}
+
+void
+EditableDenseThreeDimensionalModel::setYBinCount(size_t sz)
+{
+    m_yBinCount = sz;
+}
+
+float
+EditableDenseThreeDimensionalModel::getMinimumLevel() const
+{
+    return m_minimum;
+}
+
+void
+EditableDenseThreeDimensionalModel::setMinimumLevel(float level)
+{
+    m_minimum = level;
+}
+
+float
+EditableDenseThreeDimensionalModel::getMaximumLevel() const
+{
+    return m_maximum;
+}
+
+void
+EditableDenseThreeDimensionalModel::setMaximumLevel(float level)
+{
+    m_maximum = level;
+}
+
+void
+EditableDenseThreeDimensionalModel::getBinValues(long windowStart,
+					 BinValueSet &result) const
+{
+    QMutexLocker locker(&m_mutex);
+    
+    long index = windowStart / m_resolution;
+
+    if (index >= 0 && index < long(m_data.size())) {
+	result = m_data[index];
+    } else {
+	result.clear();
+    }
+
+    while (result.size() < m_yBinCount) result.push_back(m_minimum);
+}
+
+float
+EditableDenseThreeDimensionalModel::getBinValue(long windowStart,
+					size_t n) const
+{
+    QMutexLocker locker(&m_mutex);
+    
+    long index = windowStart / m_resolution;
+
+    if (index >= 0 && index < long(m_data.size())) {
+	const BinValueSet &s = m_data[index];
+	if (n < s.size()) return s[n];
+    }
+
+    return m_minimum;
+}
+
+void
+EditableDenseThreeDimensionalModel::setBinValues(long windowStart,
+					 const BinValueSet &values)
+{
+    QMutexLocker locker(&m_mutex);
+
+    long index = windowStart / m_resolution;
+
+    while (index >= long(m_data.size())) {
+	m_data.push_back(BinValueSet());
+    }
+
+    bool newExtents = (m_data.empty() && (m_minimum == m_maximum));
+    bool allChange = false;
+
+    for (size_t i = 0; i < values.size(); ++i) {
+	if (newExtents || values[i] < m_minimum) {
+	    m_minimum = values[i];
+	    allChange = true;
+	}
+	if (newExtents || values[i] > m_maximum) {
+	    m_maximum = values[i];
+	    allChange = true;
+	}
+    }
+
+    m_data[index] = values;
+
+    if (m_notifyOnAdd) {
+	if (allChange) {
+	    emit modelChanged();
+	} else {
+	    emit modelChanged(windowStart, windowStart + m_resolution);
+	}
+    } else {
+	if (allChange) {
+	    m_sinceLastNotifyMin = -1;
+	    m_sinceLastNotifyMax = -1;
+	    emit modelChanged();
+	} else {
+	    if (m_sinceLastNotifyMin == -1 ||
+		windowStart < m_sinceLastNotifyMin) {
+		m_sinceLastNotifyMin = windowStart;
+	    }
+	    if (m_sinceLastNotifyMax == -1 ||
+		windowStart > m_sinceLastNotifyMax) {
+		m_sinceLastNotifyMax = windowStart;
+	    }
+	}
+    }
+}
+
+QString
+EditableDenseThreeDimensionalModel::getBinName(size_t n) const
+{
+    if (m_binNames.size() > n) return m_binNames[n];
+    else return "";
+}
+
+void
+EditableDenseThreeDimensionalModel::setBinName(size_t n, QString name)
+{
+    while (m_binNames.size() <= n) m_binNames.push_back("");
+    m_binNames[n] = name;
+    emit modelChanged();
+}
+
+void
+EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
+{
+    m_binNames = names;
+    emit modelChanged();
+}
+
+void
+EditableDenseThreeDimensionalModel::setCompletion(int completion)
+{
+    if (m_completion != completion) {
+	m_completion = completion;
+
+	if (completion == 100) {
+
+	    m_notifyOnAdd = true; // henceforth
+	    emit modelChanged();
+
+	} else if (!m_notifyOnAdd) {
+
+	    if (m_sinceLastNotifyMin >= 0 &&
+		m_sinceLastNotifyMax >= 0) {
+		emit modelChanged(m_sinceLastNotifyMin,
+				  m_sinceLastNotifyMax + m_resolution);
+		m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
+	    } else {
+		emit completionChanged();
+	    }
+	} else {
+	    emit completionChanged();
+	}	    
+    }
+}
+
+void
+EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
+                                  QString indent,
+                                  QString extraAttributes) const
+{
+    // For historical reasons we read and write "resolution" as "windowSize"
+
+    out << Model::toXmlString
+	(indent, QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" %6")
+	 .arg(m_resolution)
+	 .arg(m_yBinCount)
+	 .arg(m_minimum)
+	 .arg(m_maximum)
+	 .arg(getObjectExportId(&m_data))
+	 .arg(extraAttributes));
+
+    out << indent;
+    out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
+	.arg(getObjectExportId(&m_data));
+
+    for (size_t i = 0; i < m_binNames.size(); ++i) {
+	if (m_binNames[i] != "") {
+	    out << indent + "  ";
+	    out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
+		.arg(i).arg(m_binNames[i]);
+	}
+    }
+
+    for (size_t i = 0; i < m_data.size(); ++i) {
+	out << indent + "  ";
+	out << QString("<row n=\"%1\">").arg(i);
+	for (size_t j = 0; j < m_data[i].size(); ++j) {
+	    if (j > 0) out << " ";
+	    out << m_data[i][j];
+	}
+	out << QString("</row>\n");
+    }
+
+    out << indent + "</dataset>\n";
+}
+
+QString
+EditableDenseThreeDimensionalModel::toXmlString(QString indent,
+					QString extraAttributes) const
+{
+    QString s;
+
+    {
+        QTextStream out(&s);
+        toXml(out, indent, extraAttributes);
+    }
+
+    return s;
+}
+
+#ifdef INCLUDE_MOCFILES
+#include "EditableDenseThreeDimensionalModel.moc.cpp"
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/EditableDenseThreeDimensionalModel.h	Mon Jul 31 17:05:18 2006 +0000
@@ -0,0 +1,132 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef _EDITABLE_DENSE_THREE_DIMENSIONAL_MODEL_H_
+#define _EDITABLE_DENSE_THREE_DIMENSIONAL_MODEL_H_
+
+#include "DenseThreeDimensionalModel.h"
+
+class EditableDenseThreeDimensionalModel : public DenseThreeDimensionalModel
+{
+    Q_OBJECT
+
+public:
+    EditableDenseThreeDimensionalModel(size_t sampleRate,
+				       size_t resolution,
+				       size_t yBinCount,
+				       bool notifyOnAdd = true);
+
+    virtual bool isOK() const;
+
+    virtual size_t getSampleRate() const;
+    virtual size_t getStartFrame() const;
+    virtual size_t getEndFrame() const;
+
+    virtual Model *clone() const;
+    
+
+    /**
+     * Return the number of sample frames covered by each set of bins.
+     */
+    virtual size_t getResolution() const;
+
+    /**
+     * Set the number of sample frames covered by each set of bins.
+     */
+    virtual void setResolution(size_t sz);
+
+    /**
+     * Return the number of bins in each set of bins.
+     */
+    virtual size_t getYBinCount() const; 
+
+    /**
+     * Set the number of bins in each set of bins.
+     */
+    virtual void setYBinCount(size_t sz);
+
+    /**
+     * Return the minimum value of the value in each bin.
+     */
+    virtual float getMinimumLevel() const;
+
+    /**
+     * Set the minimum value of the value in a bin.
+     */
+    virtual void setMinimumLevel(float sz);
+
+    /**
+     * Return the maximum value of the value in each bin.
+     */
+    virtual float getMaximumLevel() const;
+
+    /**
+     * Set the maximum value of the value in a bin.
+     */
+    virtual void setMaximumLevel(float sz);
+
+    typedef std::vector<float> BinValueSet;
+
+    /**
+     * Get the set of bin values at the given sample frame (i.e. the
+     * windowStartFrame/getResolution()'th set of bins).
+     */
+    virtual void getBinValues(long windowStartFrame, BinValueSet &result) const;
+
+    /**
+     * Get a single value, the one at the n'th bin of the set of bins
+     * starting at the given sample frame.
+     */
+    virtual float getBinValue(long windowStartFrame, size_t n) const;
+
+    /**
+     * Set the entire set of bin values at the given sample frame.
+     */
+    virtual void setBinValues(long windowStartFrame, const BinValueSet &values);
+
+    virtual QString getBinName(size_t n) const;
+    virtual void setBinName(size_t n, QString);
+    virtual void setBinNames(std::vector<QString> names);
+
+    virtual void setCompletion(int completion);
+    virtual int getCompletion() const { return m_completion; }
+
+    virtual void toXml(QTextStream &out,
+                       QString indent = "",
+                       QString extraAttributes = "") const;
+
+    virtual QString toXmlString(QString indent = "",
+				QString extraAttributes = "") const;
+
+protected:
+    typedef std::vector<BinValueSet> ValueMatrix;
+    ValueMatrix m_data;
+
+    std::vector<QString> m_binNames;
+
+    size_t m_sampleRate;
+    size_t m_resolution;
+    size_t m_yBinCount;
+    float m_minimum;
+    float m_maximum;
+    bool m_notifyOnAdd;
+    long m_sinceLastNotifyMin;
+    long m_sinceLastNotifyMax;
+    int m_completion;
+
+    mutable QMutex m_mutex;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/FFTModel.cpp	Mon Jul 31 17:05:18 2006 +0000
@@ -0,0 +1,126 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#include "FFTModel.h"
+#include "DenseTimeValueModel.h"
+
+#include <cassert>
+
+FFTModel::FFTModel(const DenseTimeValueModel *model,
+                   int channel,
+                   WindowType windowType,
+                   size_t windowSize,
+                   size_t windowIncrement,
+                   size_t fftSize,
+                   bool polar,
+                   size_t fillFromColumn) :
+    //!!! ZoomConstraint!
+    m_server(0),
+    m_xshift(0),
+    m_yshift(0)
+{
+    m_server = FFTDataServer::getFuzzyInstance(model,
+                                               channel,
+                                               windowType,
+                                               windowSize,
+                                               windowIncrement,
+                                               fftSize,
+                                               polar,
+                                               fillFromColumn);
+
+    size_t xratio = windowIncrement / m_server->getWindowIncrement();
+    size_t yratio = m_server->getFFTSize() / fftSize;
+
+    while (xratio > 1) {
+        if (xratio & 0x1) {
+            std::cerr << "ERROR: FFTModel: Window increment ratio "
+                      << windowIncrement << " / "
+                      << m_server->getWindowIncrement()
+                      << " must be a power of two" << std::endl;
+            assert(!(xratio & 0x1));
+        }
+        ++m_xshift;
+        xratio >>= 1;
+    }
+
+    while (yratio > 1) {
+        if (yratio & 0x1) {
+            std::cerr << "ERROR: FFTModel: FFT size ratio "
+                      << m_server->getFFTSize() << " / " << fftSize
+                      << " must be a power of two" << std::endl;
+            assert(!(yratio & 0x1));
+        }
+        ++m_yshift;
+        yratio >>= 1;
+    }
+}
+
+FFTModel::~FFTModel()
+{
+    FFTDataServer::releaseInstance(m_server);
+}
+
+size_t
+FFTModel::getSampleRate() const
+{
+    return isOK() ? m_server->getModel()->getSampleRate() : 0;
+}
+
+void
+FFTModel::getBinValues(long windowStartFrame, BinValueSet &result) const
+{
+    if (windowStartFrame < 0) windowStartFrame = 0;
+    size_t x = windowStartFrame / getResolution();
+    result.clear();
+    size_t height(getHeight());
+    for (size_t y = 0; y < height; ++y) {
+        result.push_back(const_cast<FFTModel *>(this)->getMagnitudeAt(x, y));
+    }
+}
+
+float
+FFTModel::getBinValue(long windowStartFrame, size_t n) const
+{
+    if (windowStartFrame < 0) windowStartFrame = 0;
+    size_t x = windowStartFrame / getResolution();
+    return const_cast<FFTModel *>(this)->getMagnitudeAt(x, n);
+}
+
+QString
+FFTModel::getBinName(size_t n) const
+{
+    size_t sr = getSampleRate();
+    if (!sr) return "";
+    QString name = tr("%1 Hz").arg((n * sr) / (getHeight() * 2));
+    return name;
+}
+
+Model *
+FFTModel::clone() const
+{
+    return new FFTModel(*this);
+}
+
+FFTModel::FFTModel(const FFTModel &model) :
+    QObject(),
+    ZoomConstraint(),  //!!!  want a real ZoomConstraint for this!
+    DenseThreeDimensionalModel(),
+    m_server(model.m_server),
+    m_xshift(model.m_xshift),
+    m_yshift(model.m_yshift)
+{
+    FFTDataServer::claimInstance(m_server);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/data/model/FFTModel.h	Mon Jul 31 17:05:18 2006 +0000
@@ -0,0 +1,112 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    This file copyright 2006 Chris Cannam.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+#ifndef _FFT_MODEL_H_
+#define _FFT_MODEL_H_
+
+#include "data/fft/FFTDataServer.h"
+#include "DenseThreeDimensionalModel.h"
+
+class FFTModel : public DenseThreeDimensionalModel
+{
+public:
+    FFTModel(const DenseTimeValueModel *model,
+             int channel,
+             WindowType windowType,
+             size_t windowSize,
+             size_t windowIncrement,
+             size_t fftSize,
+             bool polar,
+             size_t fillFromColumn = 0);
+    ~FFTModel();
+
+    size_t getWidth() const {
+        return m_server->getWidth() >> m_xshift;
+    }
+    size_t getHeight() const {
+        return m_server->getHeight() >> m_yshift;
+    }
+    float getMagnitudeAt(size_t x, size_t y) {
+        return m_server->getMagnitudeAt(x << m_xshift, y << m_yshift);
+    }
+    float getNormalizedMagnitudeAt(size_t x, size_t y) {
+        return m_server->getNormalizedMagnitudeAt(x << m_xshift, y << m_yshift);
+    }
+    float getMaximumMagnitudeAt(size_t x) {
+        return m_server->getMaximumMagnitudeAt(x << m_xshift);
+    }
+    float getPhaseAt(size_t x, size_t y) {
+        return m_server->getPhaseAt(x << m_xshift, y << m_yshift);
+    }
+    void getValuesAt(size_t x, size_t y, float &real, float &imaginary) {
+        m_server->getValuesAt(x << m_xshift, y << m_yshift, real, imaginary);
+    }
+    bool isColumnReady(size_t x) {
+        return m_server->isColumnReady(x << m_xshift);
+    }
+    bool isLocalPeak(size_t x, size_t y) {
+        float mag = getMagnitudeAt(x, y);
+        if (y > 0 && mag < getMagnitudeAt(x, y - 1)) return false;
+        if (y < getHeight() - 1 && mag < getMagnitudeAt(x, y + 1)) return false;
+        return true;
+    }
+    bool isOverThreshold(size_t x, size_t y, float threshold) {
+        return getMagnitudeAt(x, y) > threshold;
+    }
+
+    size_t getFillExtent() const { return m_server->getFillExtent(); }
+
+    // DenseThreeDimensionalModel and Model methods:
+    //
+    virtual bool isOK() const {
+        return m_server && m_server->getModel();
+    }
+    virtual size_t getStartFrame() const {
+        return 0;
+    }
+    virtual size_t getEndFrame() const {
+        return getWidth() * getResolution() + getResolution();
+    }
+    virtual size_t getSampleRate() const;
+    virtual size_t getResolution() const {
+        return m_server->getWindowIncrement() << m_xshift;
+    }
+    virtual size_t getYBinCount() const {
+        return getHeight();
+    }
+    virtual float getMinimumLevel() const {
+        return 0.f; // Can't provide
+    }
+    virtual float getMaximumLevel() const {
+        return 1.f; // Can't provide
+    }
+    virtual void getBinValues(long windowStartFrame, BinValueSet &result) const;
+    virtual float getBinValue(long windowStartFrame, size_t n) const;
+    virtual QString getBinName(size_t n) const;
+
+    virtual int getCompletion() const { return m_server->getFillCompletion(); }
+
+    virtual Model *clone() const;
+
+private:
+    FFTModel(const FFTModel &);
+    FFTModel &operator=(const FFTModel &); // not implemented
+
+    FFTDataServer *m_server;
+    int m_xshift;
+    int m_yshift;
+};
+
+#endif