changeset 1090:420fc961c0c4 simple-fft-model

Gut the old code, but don't replace it yet (so nothing will link yet)
author Chris Cannam
date Fri, 12 Jun 2015 14:51:46 +0100
parents 655cd4e68e9a
children bdebff3265ae
files data/fft/FFTCacheReader.h data/fft/FFTCacheStorageType.h data/fft/FFTCacheWriter.h data/fft/FFTDataServer.cpp data/fft/FFTDataServer.h data/fft/FFTFileCacheReader.cpp data/fft/FFTFileCacheReader.h data/fft/FFTFileCacheWriter.cpp data/fft/FFTFileCacheWriter.h data/fft/FFTMemoryCache.cpp data/fft/FFTMemoryCache.h data/model/FFTModel.cpp data/model/FFTModel.h svcore.pro transform/FeatureExtractionModelTransformer.cpp
diffstat 15 files changed, 78 insertions(+), 3392 deletions(-) [+]
line wrap: on
line diff
--- a/data/fft/FFTCacheReader.h	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +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-2009 Chris Cannam and QMUL.
-    
-    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_READER_H_
-#define _FFT_CACHE_READER_H_
-
-#include "FFTCacheStorageType.h"
-#include <stddef.h>
-
-class FFTCacheReader
-{
-public:
-    virtual ~FFTCacheReader() { }
-
-    virtual int getWidth() const = 0;
-    virtual int getHeight() const = 0;
-	
-    virtual float getMagnitudeAt(int x, int y) const = 0;
-    virtual float getNormalizedMagnitudeAt(int x, int y) const = 0;
-    virtual float getMaximumMagnitudeAt(int x) const = 0;
-    virtual float getPhaseAt(int x, int y) const = 0;
-
-    virtual void getValuesAt(int x, int y, float &real, float &imag) const = 0;
-    virtual void getMagnitudesAt(int x, float *values, int minbin, int count, int step) const = 0;
-
-    virtual bool haveSetColumnAt(int x) const = 0;
-
-    virtual FFTCache::StorageType getStorageType() const = 0;
-};
-
-#endif
--- a/data/fft/FFTCacheStorageType.h	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +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-2009 Chris Cannam and QMUL.
-    
-    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_STORAGE_TYPE_H_
-#define _FFT_CACHE_STORAGE_TYPE_H_
-
-namespace FFTCache {
-enum StorageType { //!!! dup
-    Compact, // 16 bits normalized polar
-    Rectangular, // floating point real+imag
-    Polar // floating point mag+phase
-};
-}
-
-#endif
--- a/data/fft/FFTCacheWriter.h	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +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-2009 Chris Cannam and QMUL.
-    
-    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_WRITER_H_
-#define _FFT_CACHE_WRITER_H_
-
-#include <stddef.h>
-
-class FFTCacheWriter
-{
-public:
-    virtual ~FFTCacheWriter() { }
-
-    virtual int getWidth() const = 0;
-    virtual int getHeight() const = 0;
-
-    virtual void setColumnAt(int x, float *mags, float *phases, float factor) = 0;
-    virtual void setColumnAt(int x, float *reals, float *imags) = 0;
-
-    virtual bool haveSetColumnAt(int x) const = 0;
-
-    virtual void allColumnsWritten() = 0; // notify cache to close
-
-    virtual FFTCache::StorageType getStorageType() const = 0;
-};
-
-#endif
-
--- a/data/fft/FFTDataServer.cpp	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1601 +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 and QMUL.
-    
-    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 "FFTDataServer.h"
-
-#include "FFTFileCacheReader.h"
-#include "FFTFileCacheWriter.h"
-#include "FFTMemoryCache.h"
-
-#include "model/DenseTimeValueModel.h"
-
-#include "system/System.h"
-
-#include "base/StorageAdviser.h"
-#include "base/Exceptions.h"
-#include "base/Profiler.h"
-#include "base/Thread.h" // for debug mutex locker
-
-#include <QWriteLocker>
-
-#include <stdexcept>
-
-//#define DEBUG_FFT_SERVER 1
-//#define DEBUG_FFT_SERVER_FILL 1
-
-#ifdef DEBUG_FFT_SERVER_FILL
-#ifndef DEBUG_FFT_SERVER
-#define DEBUG_FFT_SERVER 1
-#endif
-#endif
-
-
-FFTDataServer::ServerMap FFTDataServer::m_servers;
-FFTDataServer::ServerQueue FFTDataServer::m_releasedServers;
-QMutex FFTDataServer::m_serverMapMutex;
-
-FFTDataServer *
-FFTDataServer::getInstance(const DenseTimeValueModel *model,
-                           int channel,
-                           WindowType windowType,
-                           int windowSize,
-                           int windowIncrement,
-                           int fftSize,
-                           bool polar,
-                           StorageAdviser::Criteria criteria,
-                           sv_frame_t fillFromFrame)
-{
-    QString n = generateFileBasename(model,
-                                     channel,
-                                     windowType,
-                                     windowSize,
-                                     windowIncrement,
-                                     fftSize,
-                                     polar);
-
-    FFTDataServer *server = 0;
-    
-    MutexLocker locker(&m_serverMapMutex, "FFTDataServer::getInstance::m_serverMapMutex");
-
-    if ((server = findServer(n))) {
-        return server;
-    }
-
-    QString npn = generateFileBasename(model,
-                                       channel,
-                                       windowType,
-                                       windowSize,
-                                       windowIncrement,
-                                       fftSize,
-                                       !polar);
-
-    if ((server = findServer(npn))) {
-        return server;
-    }
-
-    try {
-        server = new FFTDataServer(n,
-                                   model,
-                                   channel,
-                                   windowType,
-                                   windowSize,
-                                   windowIncrement,
-                                   fftSize,
-                                   polar,
-                                   criteria,
-                                   fillFromFrame);
-    } catch (InsufficientDiscSpace) {
-        delete server;
-        server = 0;
-    }
-
-    if (server) {
-        m_servers[n] = ServerCountPair(server, 1);
-    }
-
-    return server;
-}
-
-FFTDataServer *
-FFTDataServer::getFuzzyInstance(const DenseTimeValueModel *model,
-                                int channel,
-                                WindowType windowType,
-                                int windowSize,
-                                int windowIncrement,
-                                int fftSize,
-                                bool polar,
-                                StorageAdviser::Criteria criteria,
-                                sv_frame_t fillFromFrame)
-{
-    // Fuzzy matching:
-    // 
-    // -- if we're asked for polar and have non-polar, use it (and
-    // vice versa).  This one is vital, and we do it for non-fuzzy as
-    // well (above).
-    //
-    // -- if we're asked for an instance with a given fft size and we
-    // have one already with a multiple of that fft size but the same
-    // window size and type (and model), we can draw the results from
-    // it (e.g. the 1st, 2nd, 3rd etc bins of a 512-sample FFT are the
-    // same as the the 1st, 5th, 9th etc of a 2048-sample FFT of the
-    // same window plus zero padding).
-    //
-    // -- if we're asked for an instance with a given window type and
-    // size and fft size and we have one already the same but with a
-    // smaller increment, we can draw the results from it (provided
-    // our increment is a multiple of its)
-    //
-    // 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 model doesn't support this).
-
-    {
-        MutexLocker locker(&m_serverMapMutex, "FFTDataServer::getFuzzyInstance::m_serverMapMutex");
-
-        ServerMap::iterator best = m_servers.end();
-        int bestdist = -1;
-    
-        for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
-
-            FFTDataServer *server = i->second.first;
-
-            if (server->getModel() == model &&
-                (server->getChannel() == channel || model->getChannelCount() == 1) &&
-                server->getWindowType() == windowType &&
-                server->getWindowSize() == windowSize &&
-                server->getWindowIncrement() <= windowIncrement &&
-                server->getFFTSize() >= fftSize) {
-                
-                if ((windowIncrement % server->getWindowIncrement()) != 0) continue;
-                int ratio = windowIncrement / server->getWindowIncrement();
-                bool poweroftwo = true;
-                while (ratio > 1) {
-                    if (ratio & 0x1) {
-                        poweroftwo = false;
-                        break;
-                    }
-                    ratio >>= 1;
-                }
-                if (!poweroftwo) continue;
-
-                if ((server->getFFTSize() % fftSize) != 0) continue;
-                ratio = server->getFFTSize() / fftSize;
-                while (ratio > 1) {
-                    if (ratio & 0x1) {
-                        poweroftwo = false;
-                        break;
-                    }
-                    ratio >>= 1;
-                }
-                if (!poweroftwo) continue;
-                
-                int distance = 0;
-                
-                if (server->getPolar() != polar) distance += 1;
-                
-                distance += ((windowIncrement / server->getWindowIncrement()) - 1) * 15;
-                distance += ((server->getFFTSize() / fftSize) - 1) * 10;
-                
-                if (server->getFillCompletion() < 50) distance += 100;
-
-#ifdef DEBUG_FFT_SERVER
-                std::cerr << "FFTDataServer::getFuzzyInstance: Distance for server " << server << " is " << distance << ", best is " << bestdist << std::endl;
-#endif
-                
-                if (bestdist == -1 || distance < bestdist) {
-                    bestdist = distance;
-                    best = i;
-                }
-            }
-        }
-
-        if (bestdist >= 0) {
-            FFTDataServer *server = best->second.first;
-#ifdef DEBUG_FFT_SERVER
-            std::cerr << "FFTDataServer::getFuzzyInstance: We like server " << server << " (with distance " << bestdist << ")" << std::endl;
-#endif
-            claimInstance(server, false);
-            return server;
-        }
-    }
-
-    // Nothing found, make a new one
-
-    return getInstance(model,
-                       channel,
-                       windowType,
-                       windowSize,
-                       windowIncrement,
-                       fftSize,
-                       polar,
-                       criteria,
-                       fillFromFrame);
-}
-
-FFTDataServer *
-FFTDataServer::findServer(QString n)
-{    
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::findServer(\"" << n << "\")" << std::endl;
-#endif
-
-    if (m_servers.find(n) != m_servers.end()) {
-
-        FFTDataServer *server = m_servers[n].first;
-
-#ifdef DEBUG_FFT_SERVER
-        std::cerr << "FFTDataServer::findServer(\"" << n << "\"): found " << server << std::endl;
-#endif
-
-        claimInstance(server, false);
-
-        return server;
-    }
-
-#ifdef DEBUG_FFT_SERVER
-        std::cerr << "FFTDataServer::findServer(\"" << n << "\"): not found" << std::endl;
-#endif
-
-    return 0;
-}
-
-void
-FFTDataServer::claimInstance(FFTDataServer *server)
-{
-    claimInstance(server, true);
-}
-
-void
-FFTDataServer::claimInstance(FFTDataServer *server, bool needLock)
-{
-    MutexLocker locker(needLock ? &m_serverMapMutex : 0,
-                       "FFTDataServer::claimInstance::m_serverMapMutex");
-
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::claimInstance(" << server << ")" << std::endl;
-#endif
-
-    for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
-        if (i->second.first == server) {
-
-            for (ServerQueue::iterator j = m_releasedServers.begin();
-                 j != m_releasedServers.end(); ++j) {
-
-                if (*j == server) {
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::claimInstance: found in released server list, removing from it" << std::endl;
-#endif
-                    m_releasedServers.erase(j);
-                    break;
-                }
-            }
-
-            ++i->second.second;
-
-#ifdef DEBUG_FFT_SERVER
-            std::cerr << "FFTDataServer::claimInstance: new refcount is " << i->second.second << std::endl;
-#endif
-
-            return;
-        }
-    }
-    
-    cerr << "ERROR: FFTDataServer::claimInstance: instance "
-              << server << " unknown!" << endl;
-}
-
-void
-FFTDataServer::releaseInstance(FFTDataServer *server)
-{
-    releaseInstance(server, true);
-}
-
-void
-FFTDataServer::releaseInstance(FFTDataServer *server, bool needLock)
-{    
-    MutexLocker locker(needLock ? &m_serverMapMutex : 0,
-                       "FFTDataServer::releaseInstance::m_serverMapMutex");
-
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::releaseInstance(" << server << ")" << std::endl;
-#endif
-
-    // -- if ref count > 0, decrement and return
-    // -- if the instance hasn't been used at all, delete it immediately 
-    // -- if fewer than N instances (N = e.g. 3) remain with zero refcounts,
-    //    leave them hanging around
-    // -- if N instances with zero refcounts remain, delete the one that
-    //    was last released first
-    // -- if we run out of disk space when allocating an instance, go back
-    //    and delete the spare N instances before trying again
-    // -- have an additional method to indicate that a model has been
-    //    destroyed, so that we can delete all of its fft server instances
-
-    for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
-        if (i->second.first == server) {
-            if (i->second.second == 0) {
-                cerr << "ERROR: FFTDataServer::releaseInstance("
-                          << server << "): instance not allocated" << endl;
-            } else if (--i->second.second == 0) {
-/*!!!
-                if (server->m_lastUsedCache == -1) { // never used
-#ifdef DEBUG_FFT_SERVER
-                    std::cerr << "FFTDataServer::releaseInstance: instance "
-                              << server << " has never been used, erasing"
-                              << std::endl;
-#endif
-                    delete server;
-                    m_servers.erase(i);
-                } else {
-*/
-#ifdef DEBUG_FFT_SERVER
-                    std::cerr << "FFTDataServer::releaseInstance: instance "
-                              << server << " no longer in use, marking for possible collection"
-                              << std::endl;
-#endif
-                    bool found = false;
-                    for (ServerQueue::iterator j = m_releasedServers.begin();
-                         j != m_releasedServers.end(); ++j) {
-                        if (*j == server) {
-                            cerr << "ERROR: FFTDataServer::releaseInstance("
-                                      << server << "): server is already in "
-                                      << "released servers list" << endl;
-                            found = true;
-                        }
-                    }
-                    if (!found) m_releasedServers.push_back(server);
-                    server->suspend();
-                    purgeLimbo();
-//!!!                }
-            } else {
-#ifdef DEBUG_FFT_SERVER
-                    std::cerr << "FFTDataServer::releaseInstance: instance "
-                              << server << " now has refcount " << i->second.second
-                              << std::endl;
-#endif
-            }
-            return;
-        }
-    }
-
-    cerr << "ERROR: FFTDataServer::releaseInstance(" << server << "): "
-              << "instance not found" << endl;
-}
-
-void
-FFTDataServer::purgeLimbo(int maxSize)
-{
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
-              << m_releasedServers.size() << " candidates" << std::endl;
-#endif
-
-    while (int(m_releasedServers.size()) > maxSize) {
-
-        FFTDataServer *server = *m_releasedServers.begin();
-
-        bool found = false;
-
-#ifdef DEBUG_FFT_SERVER
-        std::cerr << "FFTDataServer::purgeLimbo: considering candidate "
-                  << server << std::endl;
-#endif
-
-        for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
-
-            if (i->second.first == server) {
-                found = true;
-                if (i->second.second > 0) {
-                    cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
-                              << server << " is in released queue, but still has non-zero refcount "
-                              << i->second.second << endl;
-                    // ... so don't delete it
-                    break;
-                }
-#ifdef DEBUG_FFT_SERVER
-                std::cerr << "FFTDataServer::purgeLimbo: looks OK, erasing it"
-                          << std::endl;
-#endif
-
-                m_servers.erase(i);
-                delete server;
-                break;
-            }
-        }
-
-        if (!found) {
-            cerr << "ERROR: FFTDataServer::purgeLimbo: Server "
-                      << server << " is in released queue, but not in server map!"
-                      << endl;
-            delete server;
-        }
-
-        m_releasedServers.pop_front();
-    }
-
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::purgeLimbo(" << maxSize << "): "
-              << m_releasedServers.size() << " remain" << std::endl;
-#endif
-
-}
-
-void
-FFTDataServer::modelAboutToBeDeleted(Model *model)
-{
-    MutexLocker locker(&m_serverMapMutex,
-                       "FFTDataServer::modelAboutToBeDeleted::m_serverMapMutex");
-
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::modelAboutToBeDeleted(" << model << ")"
-              << std::endl;
-#endif
-
-    for (ServerMap::iterator i = m_servers.begin(); i != m_servers.end(); ++i) {
-        
-        FFTDataServer *server = i->second.first;
-
-        if (server->getModel() == model) {
-
-#ifdef DEBUG_FFT_SERVER
-            std::cerr << "FFTDataServer::modelAboutToBeDeleted: server is "
-                      << server << std::endl;
-#endif
-
-            if (i->second.second > 0) {
-                cerr << "WARNING: FFTDataServer::modelAboutToBeDeleted: Model " << model << " (\"" << model->objectName() << "\") is about to be deleted, but is still being referred to by FFT server " << server << " with non-zero refcount " << i->second.second << endl;
-                server->suspendWrites();
-                return;
-            }
-            for (ServerQueue::iterator j = m_releasedServers.begin();
-                 j != m_releasedServers.end(); ++j) {
-                if (*j == server) {
-#ifdef DEBUG_FFT_SERVER
-                    std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing from released servers" << std::endl;
-#endif
-                    m_releasedServers.erase(j);
-                    break;
-                }
-            }
-#ifdef DEBUG_FFT_SERVER
-            std::cerr << "FFTDataServer::modelAboutToBeDeleted: erasing server" << std::endl;
-#endif
-            m_servers.erase(i);
-            delete server;
-            return;
-        }
-    }
-}
-
-FFTDataServer::FFTDataServer(QString fileBaseName,
-                             const DenseTimeValueModel *model,
-                             int channel,
-			     WindowType windowType,
-			     int windowSize,
-			     int windowIncrement,
-			     int fftSize,
-                             bool polar,
-                             StorageAdviser::Criteria criteria,
-                             sv_frame_t fillFromFrame) :
-    m_fileBaseName(fileBaseName),
-    m_model(model),
-    m_channel(channel),
-    m_windower(windowType, windowSize),
-    m_windowSize(windowSize),
-    m_windowIncrement(windowIncrement),
-    m_fftSize(fftSize),
-    m_polar(polar),
-    m_width(0),
-    m_height(0),
-    m_cacheWidth(0),
-    m_cacheWidthPower(0),
-    m_cacheWidthMask(0),
-    m_criteria(criteria),
-    m_fftInput(0),
-    m_exiting(false),
-    m_suspended(true), //!!! or false?
-    m_fillThread(0)
-{
-#ifdef DEBUG_FFT_SERVER
-    cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::FFTDataServer" << endl;
-#endif
-
-    //!!! end is not correct until model finished reading -- what to do???
-
-    sv_frame_t start = m_model->getStartFrame();
-    sv_frame_t end = m_model->getEndFrame();
-
-    m_width = int((end - start) / m_windowIncrement) + 1;
-    m_height = m_fftSize / 2 + 1; // DC == 0, Nyquist == fftsize/2
-
-#ifdef DEBUG_FFT_SERVER 
-    cerr << "FFTDataServer(" << this << "): dimensions are "
-              << m_width << "x" << m_height << endl;
-#endif
-
-    int maxCacheSize = 20 * 1024 * 1024;
-    int columnSize = int(m_height * sizeof(fftsample) * 2 + sizeof(fftsample));
-    if (m_width < ((maxCacheSize * 2) / columnSize)) m_cacheWidth = m_width;
-    else m_cacheWidth = maxCacheSize / columnSize;
-    
-#ifdef DEBUG_FFT_SERVER
-    cerr << "FFTDataServer(" << this << "): cache width nominal "
-              << m_cacheWidth << ", actual ";
-#endif
-    
-    int bits = 0;
-    while (m_cacheWidth > 1) { m_cacheWidth >>= 1; ++bits; }
-    m_cacheWidthPower = bits + 1;
-    m_cacheWidth = 2;
-    while (bits) { m_cacheWidth <<= 1; --bits; }
-    m_cacheWidthMask = m_cacheWidth - 1;
-
-#ifdef DEBUG_FFT_SERVER
-    cerr << m_cacheWidth << " (power " << m_cacheWidthPower << ", mask "
-              << m_cacheWidthMask << ")" << endl;
-#endif
-
-    if (m_criteria == StorageAdviser::NoCriteria) {
-
-        // assume "spectrogram" criteria for polar ffts, and "feature
-        // extraction" criteria for rectangular ones.
-
-        if (m_polar) {
-            m_criteria = StorageAdviser::Criteria
-                (StorageAdviser::SpeedCritical |
-                 StorageAdviser::LongRetentionLikely);
-        } else {
-            m_criteria = StorageAdviser::Criteria
-                (StorageAdviser::PrecisionCritical);
-        }
-    }
-
-    for (int i = 0; i <= m_width / m_cacheWidth; ++i) {
-        m_caches.push_back(0);
-    }
-
-    m_fftInput = (fftsample *)
-        fftf_malloc(fftSize * sizeof(fftsample));
-
-    m_fftOutput = (fftf_complex *)
-        fftf_malloc((fftSize/2 + 1) * sizeof(fftf_complex));
-
-    m_workbuffer = (float *)
-        fftf_malloc((fftSize+2) * sizeof(float));
-
-    m_fftPlan = fftf_plan_dft_r2c_1d(m_fftSize,
-                                     m_fftInput,
-                                     m_fftOutput,
-                                     FFTW_MEASURE);
-
-    if (!m_fftPlan) {
-        cerr << "ERROR: fftf_plan_dft_r2c_1d(" << m_windowSize << ") failed!" << endl;
-        throw(0);
-    }
-
-    m_fillThread = new FillThread(*this, fillFromFrame);
-}
-
-FFTDataServer::~FFTDataServer()
-{
-#ifdef DEBUG_FFT_SERVER
-    cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "])::~FFTDataServer()" << endl;
-#endif
-
-    m_suspended = false;
-    m_exiting = true;
-    m_condition.wakeAll();
-    if (m_fillThread) {
-        m_fillThread->wait();
-        delete m_fillThread;
-    }
-
-//    MutexLocker locker(&m_writeMutex,
-//                       "FFTDataServer::~FFTDataServer::m_writeMutex");
-
-    QMutexLocker mlocker(&m_fftBuffersLock);
-    QWriteLocker wlocker(&m_cacheVectorLock);
-
-    for (CacheVector::iterator i = m_caches.begin(); i != m_caches.end(); ++i) {
-        if (*i) {
-            delete *i;
-        }
-    }
-
-    deleteProcessingData();
-}
-
-void
-FFTDataServer::deleteProcessingData()
-{
-#ifdef DEBUG_FFT_SERVER
-    cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): deleteProcessingData" << endl;
-#endif
-    if (m_fftInput) {
-        fftf_destroy_plan(m_fftPlan);
-        fftf_free(m_fftInput);
-        fftf_free(m_fftOutput);
-        fftf_free(m_workbuffer);
-    }
-    m_fftInput = 0;
-}
-
-void
-FFTDataServer::suspend()
-{
-#ifdef DEBUG_FFT_SERVER
-    cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspend" << endl;
-#endif
-    Profiler profiler("FFTDataServer::suspend", false);
-
-    QMutexLocker locker(&m_fftBuffersLock);
-    m_suspended = true;
-}
-
-void
-FFTDataServer::suspendWrites()
-{
-#ifdef DEBUG_FFT_SERVER
-    cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspendWrites" << endl;
-#endif
-    Profiler profiler("FFTDataServer::suspendWrites", false);
-
-    m_suspended = true;
-}
-
-void
-FFTDataServer::resume()
-{
-#ifdef DEBUG_FFT_SERVER
-    cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): resume" << endl;
-#endif
-    Profiler profiler("FFTDataServer::resume", false);
-
-    m_suspended = false;
-    if (m_fillThread) {
-        if (m_fillThread->isFinished()) {
-            delete m_fillThread;
-            m_fillThread = 0;
-            deleteProcessingData();
-        } else if (!m_fillThread->isRunning()) {
-            m_fillThread->start();
-        } else {
-            m_condition.wakeAll();
-        }
-    }
-}
-
-void
-FFTDataServer::getStorageAdvice(int w, int h,
-                                bool &memoryCache, bool &compactCache)
-{
-    if (w < 0 || h < 0) throw std::domain_error("width & height must be non-negative");
-    size_t cells = size_t(w) * h;
-    size_t minimumSize = (cells / 1024) * sizeof(uint16_t); // kb
-    size_t maximumSize = (cells / 1024) * sizeof(float); // kb
-
-    // We don't have a compact rectangular representation, and compact
-    // of course is never precision-critical
-
-    bool canCompact = true;
-    if ((m_criteria & StorageAdviser::PrecisionCritical) || !m_polar) {
-        canCompact = false;
-        minimumSize = maximumSize; // don't use compact
-    }
-    
-    StorageAdviser::Recommendation recommendation;
-
-    try {
-
-        recommendation =
-            StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
-
-    } catch (InsufficientDiscSpace s) {
-
-        // Delete any unused servers we may have been leaving around
-        // in case we wanted them again
-
-        purgeLimbo(0);
-
-        // This time we don't catch InsufficientDiscSpace -- we
-        // haven't allocated anything yet and can safely let the
-        // exception out to indicate to the caller that we can't
-        // handle it.
-
-        recommendation =
-            StorageAdviser::recommend(m_criteria, minimumSize, maximumSize);
-    }
-
-//    cerr << "Recommendation was: " << recommendation << endl;
-
-    memoryCache = false;
-
-    if ((recommendation & StorageAdviser::UseMemory) ||
-        (recommendation & StorageAdviser::PreferMemory)) {
-        memoryCache = true;
-    }
-
-    compactCache = canCompact &&
-        (recommendation & StorageAdviser::ConserveSpace);
-
-#ifdef DEBUG_FFT_SERVER
-    cerr << "FFTDataServer: memory cache = " << memoryCache << ", compact cache = " << compactCache << endl;
-    
-    cerr << "Width " << w << " of " << m_width << ", height " << h << ", size " << w * h << endl;
-#endif
-}
-
-bool
-FFTDataServer::makeCache(int c)
-{
-    // Creating the cache could take a significant amount of time.  We
-    // don't want to block readers on m_cacheVectorLock while this is
-    // happening, but we do want to block any further calls to
-    // makeCache.  So we use this lock solely to serialise this
-    // particular function -- it isn't used anywhere else.
-
-    QMutexLocker locker(&m_cacheCreationMutex);
-
-    m_cacheVectorLock.lockForRead();
-    if (m_caches[c]) {
-        // someone else must have created the cache between our
-        // testing for it and taking the mutex
-        m_cacheVectorLock.unlock();
-        return true;
-    }
-    m_cacheVectorLock.unlock();
-
-    // Now m_cacheCreationMutex is held, but m_cacheVectorLock is not
-    // -- readers can proceed, but callers to this function will block
-
-    CacheBlock *cb = new CacheBlock;
-
-    QString name = QString("%1-%2").arg(m_fileBaseName).arg(c);
-
-    int width = m_cacheWidth;
-    if (c * m_cacheWidth + width > m_width) {
-        width = m_width - c * m_cacheWidth;
-    }
-
-    bool memoryCache = false;
-    bool compactCache = false;
-
-    getStorageAdvice(width, m_height, memoryCache, compactCache);
-
-    bool success = false;
-
-    if (memoryCache) {
-
-        try {
-
-            cb->memoryCache = new FFTMemoryCache
-                (compactCache ? FFTCache::Compact :
-                      m_polar ? FFTCache::Polar :
-                                FFTCache::Rectangular,
-                 width, m_height);
-
-            success = true;
-
-        } catch (std::bad_alloc) {
-
-            delete cb->memoryCache;
-            cb->memoryCache = 0;
-            
-            cerr << "WARNING: Memory allocation failed when creating"
-                      << " FFT memory cache no. " << c << " of " << width 
-                      << "x" << m_height << " (of total width " << m_width
-                      << "): falling back to disc cache" << endl;
-
-            memoryCache = false;
-        }
-    }
-
-    if (!memoryCache) {
-
-        try {
-        
-            cb->fileCacheWriter = new FFTFileCacheWriter
-                (name,
-                 compactCache ? FFTCache::Compact :
-                      m_polar ? FFTCache::Polar :
-                                FFTCache::Rectangular,
-                 width, m_height);
-
-            success = true;
-
-        } catch (std::exception &e) {
-
-            delete cb->fileCacheWriter;
-            cb->fileCacheWriter = 0;
-            
-            cerr << "ERROR: Failed to construct disc cache for FFT data: "
-                      << e.what() << endl;
-
-            throw;
-        }
-    }
-
-    m_cacheVectorLock.lockForWrite();
-
-    m_caches[c] = cb;
-
-    m_cacheVectorLock.unlock();
-
-    return success;
-}
- 
-bool
-FFTDataServer::makeCacheReader(int c)
-{
-    // preconditions: m_caches[c] exists and contains a file writer;
-    // m_cacheVectorLock is not locked by this thread
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::makeCacheReader(" << c << ")" << std::endl;
-#endif
-
-    QThread *me = QThread::currentThread();
-    QWriteLocker locker(&m_cacheVectorLock);
-    CacheBlock *cb(m_caches.at(c));
-    if (!cb || !cb->fileCacheWriter) return false;
-
-    try {
-        
-        cb->fileCacheReader[me] = new FFTFileCacheReader(cb->fileCacheWriter);
-
-    } catch (std::exception &e) {
-
-        delete cb->fileCacheReader[me];
-        cb->fileCacheReader.erase(me);
-            
-        cerr << "ERROR: Failed to construct disc cache reader for FFT data: "
-                  << e.what() << endl;
-        return false;
-    }
-
-    // erase a reader that looks like it may no longer going to be
-    // used by this thread for a while (leaving alone the current
-    // and previous cache readers)
-    int deleteCandidate = c - 2;
-    if (deleteCandidate < 0) deleteCandidate = c + 2;
-    if (deleteCandidate >= (int)m_caches.size()) {
-        return true;
-    }
-
-    cb = m_caches.at(deleteCandidate);
-    if (cb && cb->fileCacheReader.find(me) != cb->fileCacheReader.end()) {
-#ifdef DEBUG_FFT_SERVER
-        std::cerr << "FFTDataServer::makeCacheReader: Deleting probably unpopular reader " << deleteCandidate << " for this thread (as I create reader " << c << ")" << std::endl;
-#endif
-        delete cb->fileCacheReader[me];
-        cb->fileCacheReader.erase(me);
-    }
-            
-    return true;
-}
-       
-float
-FFTDataServer::getMagnitudeAt(int x, int y)
-{
-    Profiler profiler("FFTDataServer::getMagnitudeAt", false);
-
-    if (x >= m_width || y >= m_height) return 0;
-
-    float val = 0;
-
-    try {
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return 0;
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") return false;
-            Profiler profiler("FFTDataServer::getMagnitudeAt: filling");
-#ifdef DEBUG_FFT_SERVER
-            std::cerr << "FFTDataServer::getMagnitudeAt: calling fillColumn("
-                  << x << ")" << std::endl;
-#endif
-            fillColumn(x);
-        }
-
-        val = cache->getMagnitudeAt(col, y);
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-    }
-
-    return val;
-}
-
-bool
-FFTDataServer::getMagnitudesAt(int x, float *values, int minbin, int count, int step)
-{
-    Profiler profiler("FFTDataServer::getMagnitudesAt", false);
-
-    if (x >= m_width) return false;
-
-    if (minbin >= m_height) minbin = m_height - 1;
-    if (count == 0) count = (m_height - minbin) / step;
-    else if (minbin + count * step > m_height) {
-        count = (m_height - minbin) / step;
-    }
-
-    try {
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return false;
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") return false;
-            Profiler profiler("FFTDataServer::getMagnitudesAt: filling");
-            fillColumn(x);
-        }
-
-        cache->getMagnitudesAt(col, values, minbin, count, step);
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-        return false;
-    }
-
-    return true;
-}
-
-float
-FFTDataServer::getNormalizedMagnitudeAt(int x, int y)
-{
-    Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt", false);
-
-    if (x >= m_width || y >= m_height) return 0;
-
-    float val = 0;
-
-    try {
-
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return 0;
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") return false;
-            Profiler profiler("FFTDataServer::getNormalizedMagnitudeAt: filling");
-            fillColumn(x);
-        }
-        val = cache->getNormalizedMagnitudeAt(col, y);
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-    }
-
-    return val;
-}
-
-bool
-FFTDataServer::getNormalizedMagnitudesAt(int x, float *values, int minbin, int count, int step)
-{
-    Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt", false);
-
-    if (x >= m_width) return false;
-
-    if (minbin >= m_height) minbin = m_height - 1;
-    if (count == 0) count = (m_height - minbin) / step;
-    else if (minbin + count * step > m_height) {
-        count = (m_height - minbin) / step;
-    }
-
-    try {
-
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return false;
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") return false;
-            Profiler profiler("FFTDataServer::getNormalizedMagnitudesAt: filling");
-            fillColumn(x);
-        }
-        
-        for (int i = 0; i < count; ++i) {
-            values[i] = cache->getNormalizedMagnitudeAt(col, i * step + minbin);
-        }
-        
-    } catch (std::exception &e) {
-        m_error = e.what();
-        return false;
-    }
-
-    return true;
-}
-
-float
-FFTDataServer::getMaximumMagnitudeAt(int x)
-{
-    Profiler profiler("FFTDataServer::getMaximumMagnitudeAt", false);
-
-    if (x >= m_width) return 0;
-
-    float val = 0;
-
-    try {
-
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return 0;
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") return false;
-            Profiler profiler("FFTDataServer::getMaximumMagnitudeAt: filling");
-            fillColumn(x);
-        }
-        val = cache->getMaximumMagnitudeAt(col);
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-    }
-
-    return val;
-}
-
-float
-FFTDataServer::getPhaseAt(int x, int y)
-{
-    Profiler profiler("FFTDataServer::getPhaseAt", false);
-
-    if (x >= m_width || y >= m_height) return 0;
-
-    float val = 0;
-
-    try {
-
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return 0;
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") return false;
-            Profiler profiler("FFTDataServer::getPhaseAt: filling");
-            fillColumn(x);
-        }
-        val = cache->getPhaseAt(col, y);
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-    }
-
-    return val;
-}
-
-bool
-FFTDataServer::getPhasesAt(int x, float *values, int minbin, int count, int step)
-{
-    Profiler profiler("FFTDataServer::getPhasesAt", false);
-
-    if (x >= m_width) return false;
-
-    if (minbin >= m_height) minbin = m_height - 1;
-    if (count == 0) count = (m_height - minbin) / step;
-    else if (minbin + count * step > m_height) {
-        count = (m_height - minbin) / step;
-    }
-
-    try {
-
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return false;
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") return false;
-            Profiler profiler("FFTDataServer::getPhasesAt: filling");
-            fillColumn(x);
-        }
-        
-        for (int i = 0; i < count; ++i) {
-            values[i] = cache->getPhaseAt(col, i * step + minbin);
-        }
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-        return false;
-    }
-
-    return true;
-}
-
-void
-FFTDataServer::getValuesAt(int x, int y, float &real, float &imaginary)
-{
-    Profiler profiler("FFTDataServer::getValuesAt", false);
-
-    if (x >= m_width || y >= m_height) {
-        real = 0;
-        imaginary = 0;
-        return;
-    }
-
-    try {
-
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-
-        if (!cache) {
-            real = 0;
-            imaginary = 0;
-            return;
-        }
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") {
-                real = 0;
-                imaginary = 0;
-                return;
-            }
-            Profiler profiler("FFTDataServer::getValuesAt: filling");
-#ifdef DEBUG_FFT_SERVER
-            std::cerr << "FFTDataServer::getValuesAt(" << x << ", " << y << "): filling" << std::endl;
-#endif
-            fillColumn(x);
-        }        
-
-        cache->getValuesAt(col, y, real, imaginary);
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-    }
-}
-
-bool
-FFTDataServer::getValuesAt(int x, float *reals, float *imaginaries, int minbin, int count, int step)
-{
-    Profiler profiler("FFTDataServer::getValuesAt", false);
-
-    if (x >= m_width) return false;
-
-    if (minbin >= m_height) minbin = m_height - 1;
-    if (count == 0) count = (m_height - minbin) / step;
-    else if (minbin + count * step > m_height) {
-        count = (m_height - minbin) / step;
-    }
-
-    try {
-
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return false;
-
-        if (!cache->haveSetColumnAt(col)) {
-            if (getError() != "") return false;
-            Profiler profiler("FFTDataServer::getValuesAt: filling");
-            fillColumn(x);
-        }
-
-        for (int i = 0; i < count; ++i) {
-            cache->getValuesAt(col, i * step + minbin, reals[i], imaginaries[i]);
-        }
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-        return false;
-    }
-
-    return true;
-}
-
-bool
-FFTDataServer::isColumnReady(int x)
-{
-    Profiler profiler("FFTDataServer::isColumnReady", false);
-
-    if (x >= m_width) return true;
-
-    if (!haveCache(x)) {
-/*!!!
-        if (m_lastUsedCache == -1) {
-            if (m_suspended) {
-                std::cerr << "FFTDataServer::isColumnReady(" << x << "): no cache, calling resume" << std::endl;
-                resume();
-            }
-            m_fillThread->start();
-        }
-*/
-        return false;
-    }
-
-    try {
-
-        int col;
-        FFTCacheReader *cache = getCacheReader(x, col);
-        if (!cache) return true;
-
-        return cache->haveSetColumnAt(col);
-
-    } catch (std::exception &e) {
-        m_error = e.what();
-        return false;
-    }
-}    
-
-void
-FFTDataServer::fillColumn(int x)
-{
-    Profiler profiler("FFTDataServer::fillColumn", false);
-
-    if (!m_model->isReady()) {
-        cerr << "WARNING: FFTDataServer::fillColumn(" 
-                  << x << "): model not yet ready" << endl;
-        return;
-    }
-/*
-    if (!m_fftInput) {
-        cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
-                  << "input has already been completed and discarded?"
-                  << endl;
-        return;
-    }
-*/
-    if (x >= m_width) {
-        cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
-                  << "x > width (" << x << " > " << m_width << ")"
-                  << endl;
-        return;
-    }
-
-    int col;
-#ifdef DEBUG_FFT_SERVER_FILL
-    cout << "FFTDataServer::fillColumn(" << x << ")" << endl;
-#endif
-    FFTCacheWriter *cache = getCacheWriter(x, col);
-    if (!cache) return;
-
-    int winsize = m_windowSize;
-    int fftsize = m_fftSize;
-    int hs = fftsize/2;
-
-    sv_frame_t pfx = 0;
-    int off = (fftsize - winsize) / 2;
-
-    sv_frame_t startFrame = m_windowIncrement * sv_frame_t(x);
-    sv_frame_t endFrame = startFrame + m_windowSize;
-
-    // FFT windows are centred at the respective audio sample frame,
-    // so the first one is centred at 0
-    startFrame -= winsize / 2;
-    endFrame   -= winsize / 2;
-
-#ifdef DEBUG_FFT_SERVER_FILL
-    std::cerr << "FFTDataServer::fillColumn: requesting frames "
-              << startFrame + pfx << " -> " << endFrame << " ( = "
-              << endFrame - (startFrame + pfx) << ") at index "
-              << off + pfx << " in buffer of size " << m_fftSize
-              << " with window size " << m_windowSize 
-              << " from channel " << m_channel << std::endl;
-#endif
-
-    QMutexLocker locker(&m_fftBuffersLock);
-
-    // We may have been called from a function that wanted to obtain a
-    // column using an FFTCacheReader.  Before calling us, it checked
-    // whether the column was available already, and the reader
-    // reported that it wasn't.  Now we test again, with the mutex
-    // held, to avoid a race condition in case another thread has
-    // called fillColumn at the same time.
-    if (cache->haveSetColumnAt(x & m_cacheWidthMask)) {
-        return;
-    }
-
-    if (!m_fftInput) {
-        cerr << "WARNING: FFTDataServer::fillColumn(" << x << "): "
-                  << "input has already been completed and discarded?"
-                  << endl;
-        return;
-    }
-
-    for (int i = 0; i < off; ++i) {
-        m_fftInput[i] = 0.0;
-    }
-
-    for (int i = 0; i < off; ++i) {
-        m_fftInput[fftsize - i - 1] = 0.0;
-    }
-
-    if (startFrame < 0) {
-	pfx = -startFrame;
-	for (int i = 0; i < pfx; ++i) {
-	    m_fftInput[off + i] = 0.0;
-	}
-    }
-
-    sv_frame_t count = 0;
-    if (endFrame > startFrame + pfx) count = endFrame - (startFrame + pfx);
-
-    sv_frame_t got = m_model->getData(m_channel, startFrame + pfx,
-                                      count, m_fftInput + off + pfx);
-
-    while (got + pfx < winsize) {
-	m_fftInput[off + got + pfx] = 0.0;
-	++got;
-    }
-
-    if (m_channel == -1) {
-	int channels = m_model->getChannelCount();
-	if (channels > 1) {
-	    for (int i = 0; i < winsize; ++i) {
-		m_fftInput[off + i] /= float(channels);
-	    }
-	}
-    }
-
-    m_windower.cut(m_fftInput + off);
-
-    for (int i = 0; i < hs; ++i) {
-	fftsample temp = m_fftInput[i];
-	m_fftInput[i] = m_fftInput[i + hs];
-	m_fftInput[i + hs] = temp;
-    }
-
-    fftf_execute(m_fftPlan);
-
-    float factor = 0.f;
-
-    if (cache->getStorageType() == FFTCache::Compact ||
-        cache->getStorageType() == FFTCache::Polar) {
-
-        for (int i = 0; i <= hs; ++i) {
-            fftsample real = m_fftOutput[i][0];
-            fftsample imag = m_fftOutput[i][1];
-            float mag = sqrtf(real * real + imag * imag);
-            m_workbuffer[i] = mag;
-            m_workbuffer[i + hs + 1] = atan2f(imag, real);
-            if (mag > factor) factor = mag;
-        }
-
-    } else {
-
-        for (int i = 0; i <= hs; ++i) {
-            m_workbuffer[i] = m_fftOutput[i][0];
-            m_workbuffer[i + hs + 1] = m_fftOutput[i][1];
-        }
-    }
-
-    Profiler subprof("FFTDataServer::fillColumn: set to cache");
-
-    if (cache->getStorageType() == FFTCache::Compact ||
-        cache->getStorageType() == FFTCache::Polar) {
-            
-        cache->setColumnAt(col,
-                           m_workbuffer,
-                           m_workbuffer + hs + 1,
-                           factor);
-
-    } else {
-
-        cache->setColumnAt(col,
-                           m_workbuffer,
-                           m_workbuffer + hs + 1);
-    }
-
-    if (m_suspended) {
-//        std::cerr << "FFTDataServer::fillColumn(" << x << "): calling resume" << std::endl;
-//        resume();
-    }
-}    
-
-void
-FFTDataServer::fillComplete()
-{
-    for (int i = 0; i < int(m_caches.size()); ++i) {
-        if (!m_caches[i]) continue;
-        if (m_caches[i]->memoryCache) {
-            m_caches[i]->memoryCache->allColumnsWritten();
-        }
-        if (m_caches[i]->fileCacheWriter) {
-            m_caches[i]->fileCacheWriter->allColumnsWritten();
-        }
-    }
-}
-
-QString
-FFTDataServer::getError() const
-{
-    QString err;
-    if (m_error != "") {
-        err = m_error;
-//        cerr << "FFTDataServer::getError: err (server " << this << ") = " << err << endl;
-    } else {
-        MutexLocker locker(&m_fftBuffersLock, "FFTDataServer::getError");
-        if (m_fillThread) {
-            err = m_fillThread->getError();
-//            cerr << "FFTDataServer::getError: err (server " << this << ", from thread " << m_fillThread
-//                 << ") = " << err << endl;
-        }
-    }
-    return err;
-}
-
-int
-FFTDataServer::getFillCompletion() const 
-{
-    if (m_fillThread) return m_fillThread->getCompletion();
-    else return 100;
-}
-
-sv_frame_t
-FFTDataServer::getFillExtent() const
-{
-    if (m_fillThread) return m_fillThread->getExtent();
-    else return m_model->getEndFrame();
-}
-
-QString
-FFTDataServer::generateFileBasename() const
-{
-    return generateFileBasename(m_model, m_channel, m_windower.getType(),
-                                m_windowSize, m_windowIncrement, m_fftSize,
-                                m_polar);
-}
-
-QString
-FFTDataServer::generateFileBasename(const DenseTimeValueModel *model,
-                                    int channel,
-                                    WindowType windowType,
-                                    int windowSize,
-                                    int windowIncrement,
-                                    int fftSize,
-                                    bool polar)
-{
-    return QString("%1-%2-%3-%4-%5-%6%7")
-        .arg(XmlExportable::getObjectExportId(model))
-        .arg(channel + 1)
-        .arg((int)windowType)
-        .arg(windowSize)
-        .arg(windowIncrement)
-        .arg(fftSize)
-        .arg(polar ? "-p" : "-r");
-}
-
-void
-FFTDataServer::FillThread::run()
-{
-#ifdef DEBUG_FFT_SERVER_FILL
-    std::cerr << "FFTDataServer::FillThread::run()" << std::endl;
-#endif
-    
-    m_extent = 0;
-    m_completion = 0;
-    
-    while (!m_server.m_model->isReady() && !m_server.m_exiting) {
-#ifdef DEBUG_FFT_SERVER_FILL
-        std::cerr << "FFTDataServer::FillThread::run(): waiting for model " << m_server.m_model << " to be ready" << std::endl;
-#endif
-        sleep(1);
-    }
-    if (m_server.m_exiting) return;
-
-    sv_frame_t start = m_server.m_model->getStartFrame();
-    sv_frame_t end = m_server.m_model->getEndFrame();
-    sv_frame_t remainingEnd = end;
-
-    int counter = 0;
-    int updateAt = 1;
-    int maxUpdateAt = int(end / m_server.m_windowIncrement) / 20;
-    if (maxUpdateAt < 100) maxUpdateAt = 100;
-
-    if (m_fillFrom > start) {
-
-        for (sv_frame_t f = m_fillFrom; f < end; f += m_server.m_windowIncrement) {
-	    
-            try {
-                m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
-            } catch (std::exception &e) {
-                MutexLocker locker(&m_server.m_fftBuffersLock,
-                                   "FFTDataServer::run::m_fftBuffersLock [err]");
-                m_threadError = e.what();
-                std::cerr << "FFTDataServer::FillThread::run: exception: " << m_threadError << " (thread = " << this << " from server " << &m_server << ")" << std::endl;
-                m_server.fillComplete();
-                m_completion = 100;
-                m_extent = end;
-                return;
-            }
-
-            if (m_server.m_exiting) return;
-
-            while (m_server.m_suspended) {
-#ifdef DEBUG_FFT_SERVER
-                cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << endl;
-#endif
-                MutexLocker locker(&m_server.m_fftBuffersLock,
-                                   "FFTDataServer::run::m_fftBuffersLock [1]");
-                if (m_server.m_suspended && !m_server.m_exiting) {
-                    m_server.m_condition.wait(&m_server.m_fftBuffersLock, 10000);
-                }
-#ifdef DEBUG_FFT_SERVER
-                cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): waited" << endl;
-#endif
-                if (m_server.m_exiting) return;
-            }
-
-            if (++counter == updateAt) {
-                m_extent = f;
-                m_completion = int(100 * fabsf(float(f - m_fillFrom) /
-                                                  float(end - start)));
-                counter = 0;
-                if (updateAt < maxUpdateAt) {
-                    updateAt *= 2;
-                    if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
-                }
-            }
-        }
-
-        remainingEnd = m_fillFrom;
-        if (remainingEnd > start) --remainingEnd;
-        else remainingEnd = start;
-    }
-
-    int baseCompletion = m_completion;
-
-    for (sv_frame_t f = start; f < remainingEnd; f += m_server.m_windowIncrement) {
-
-        try {
-            m_server.fillColumn(int((f - start) / m_server.m_windowIncrement));
-        } catch (std::exception &e) {
-            MutexLocker locker(&m_server.m_fftBuffersLock,
-                               "FFTDataServer::run::m_fftBuffersLock [err]");
-            m_threadError = e.what();
-                std::cerr << "FFTDataServer::FillThread::run: exception: " << m_threadError << " (thread = " << this << " from server " << &m_server << ")" << std::endl;
-            m_server.fillComplete();
-            m_completion = 100;
-            m_extent = end;
-            return;
-        }
-
-        if (m_server.m_exiting) return;
-
-        while (m_server.m_suspended) {
-#ifdef DEBUG_FFT_SERVER
-            cerr << "FFTDataServer(" << this << " [" << (void *)QThread::currentThreadId() << "]): suspended, waiting..." << endl;
-#endif
-            {
-                MutexLocker locker(&m_server.m_fftBuffersLock,
-                                   "FFTDataServer::run::m_fftBuffersLock [2]");
-                if (m_server.m_suspended && !m_server.m_exiting) {
-                    m_server.m_condition.wait(&m_server.m_fftBuffersLock, 10000);
-                }
-            }
-            if (m_server.m_exiting) return;
-        }
-		    
-        if (++counter == updateAt) {
-            m_extent = f;
-            m_completion = baseCompletion +
-                int(100 * fabsf(float(f - start) /
-                                   float(end - start)));
-            counter = 0;
-            if (updateAt < maxUpdateAt) {
-                updateAt *= 2;
-                if (updateAt > maxUpdateAt) updateAt = maxUpdateAt;
-            }
-        }
-    }
-
-    m_server.fillComplete();
-    m_completion = 100;
-    m_extent = end;
-
-#ifdef DEBUG_FFT_SERVER
-    std::cerr << "FFTDataServer::FillThread::run exiting" << std::endl;
-#endif
-}
-
--- a/data/fft/FFTDataServer.h	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +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 and QMUL.
-    
-    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_DATA_SERVER_H_
-#define _FFT_DATA_SERVER_H_
-
-#include "base/Window.h"
-#include "base/Thread.h"
-#include "base/StorageAdviser.h"
-
-#include "FFTapi.h"
-#include "FFTFileCacheReader.h"
-#include "FFTFileCacheWriter.h"
-#include "FFTMemoryCache.h"
-
-#include <QMutex>
-#include <QReadWriteLock>
-#include <QReadLocker>
-#include <QWaitCondition>
-#include <QString>
-
-#include <vector>
-#include <deque>
-
-class DenseTimeValueModel;
-class Model;
-
-class FFTDataServer
-{
-public:
-    static FFTDataServer *getInstance(const DenseTimeValueModel *model,
-                                      int channel,
-                                      WindowType windowType,
-                                      int windowSize,
-                                      int windowIncrement,
-                                      int fftSize,
-                                      bool polar,
-                                      StorageAdviser::Criteria criteria =
-                                          StorageAdviser::NoCriteria,
-                                      sv_frame_t fillFromFrame = 0);
-
-    static FFTDataServer *getFuzzyInstance(const DenseTimeValueModel *model,
-                                           int channel,
-                                           WindowType windowType,
-                                           int windowSize,
-                                           int windowIncrement,
-                                           int fftSize,
-                                           bool polar,
-                                           StorageAdviser::Criteria criteria =
-                                               StorageAdviser::NoCriteria,
-                                           sv_frame_t fillFromFrame = 0);
-
-    static void claimInstance(FFTDataServer *);
-    static void releaseInstance(FFTDataServer *);
-
-    static void modelAboutToBeDeleted(Model *);
-
-    const DenseTimeValueModel *getModel() const { return m_model; }
-    int        getChannel() const { return m_channel; }
-    WindowType getWindowType() const { return m_windower.getType(); }
-    int        getWindowSize() const { return m_windowSize; }
-    int        getWindowIncrement() const { return m_windowIncrement; }
-    int        getFFTSize() const { return m_fftSize; }
-    bool       getPolar() const { return m_polar; }
-
-    int        getWidth() const  { return m_width;  }
-    int        getHeight() const { return m_height; }
-
-    float      getMagnitudeAt(int x, int y);
-    float      getNormalizedMagnitudeAt(int x, int y);
-    float      getMaximumMagnitudeAt(int x);
-    float      getPhaseAt(int x, int y);
-    void       getValuesAt(int x, int y, float &real, float &imaginary);
-    bool       isColumnReady(int x);
-
-    bool       getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1);
-    bool       getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1);
-    bool       getPhasesAt(int x, float *values, int minbin = 0, int count = 0, int step = 1);
-    bool       getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0, int step = 1);
-
-    void       suspend();
-    void       suspendWrites();
-    void       resume(); // also happens automatically if new data needed
-
-    // Convenience functions:
-
-    bool isLocalPeak(int x, int 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(int x, int y, float threshold) {
-        return getMagnitudeAt(x, y) > threshold;
-    }
-
-    QString getError() const;
-    int getFillCompletion() const;
-    sv_frame_t getFillExtent() const;
-
-private:
-    FFTDataServer(QString fileBaseName,
-                  const DenseTimeValueModel *model,
-                  int channel,
-                  WindowType windowType,
-                  int windowSize,
-                  int windowIncrement,
-                  int fftSize,
-                  bool polar,
-                  StorageAdviser::Criteria criteria,
-                  sv_frame_t fillFromFrame = 0);
-
-    virtual ~FFTDataServer();
-
-    FFTDataServer(const FFTDataServer &); // not implemented
-    FFTDataServer &operator=(const FFTDataServer &); // not implemented
-
-    typedef float fftsample;
-
-    QString m_fileBaseName;
-    const DenseTimeValueModel *m_model;
-    int m_channel;
-
-    Window<fftsample> m_windower;
-
-    int m_windowSize;
-    int m_windowIncrement;
-    int m_fftSize;
-    bool m_polar;
-
-    int m_width;
-    int m_height;
-    int m_cacheWidth;
-    int m_cacheWidthPower;
-    int m_cacheWidthMask;
-
-    struct CacheBlock {
-        FFTMemoryCache *memoryCache;
-        typedef std::map<QThread *, FFTFileCacheReader *> ThreadReaderMap;
-        ThreadReaderMap fileCacheReader;
-        FFTFileCacheWriter *fileCacheWriter;
-        CacheBlock() : memoryCache(0), fileCacheWriter(0) { }
-        ~CacheBlock() {
-            delete memoryCache; 
-            while (!fileCacheReader.empty()) {
-                delete fileCacheReader.begin()->second;
-                fileCacheReader.erase(fileCacheReader.begin());
-            }
-            delete fileCacheWriter;
-        }
-    };
-
-    typedef std::vector<CacheBlock *> CacheVector;
-    CacheVector m_caches;
-    QReadWriteLock m_cacheVectorLock; // locks cache lookup, not use
-    QMutex m_cacheCreationMutex; // solely to serialise makeCache() calls
-
-    FFTCacheReader *getCacheReader(int x, int &col) {
-        Profiler profiler("FFTDataServer::getCacheReader");
-        col = x & m_cacheWidthMask;
-        int c = x >> m_cacheWidthPower;
-        m_cacheVectorLock.lockForRead();
-        CacheBlock *cb(m_caches.at(c));
-        if (cb) {
-            if (cb->memoryCache) {
-                m_cacheVectorLock.unlock();
-                return cb->memoryCache;
-            }
-            if (cb->fileCacheWriter) {
-                QThread *me = QThread::currentThread();
-                CacheBlock::ThreadReaderMap &map = cb->fileCacheReader;
-                if (map.find(me) == map.end()) {
-                    m_cacheVectorLock.unlock();
-                    if (!makeCacheReader(c)) return 0;
-                    return getCacheReader(x, col);
-                }
-                FFTCacheReader *reader = cb->fileCacheReader[me];
-                m_cacheVectorLock.unlock();
-                return reader;
-            }
-            // if cb exists but cb->fileCacheWriter doesn't, creation
-            // must have failed: don't try again
-            m_cacheVectorLock.unlock();
-            return 0;
-        }
-        m_cacheVectorLock.unlock();
-        if (getError() != "") return 0;
-        if (!makeCache(c)) return 0;
-        return getCacheReader(x, col);
-    }
-    
-    FFTCacheWriter *getCacheWriter(int x, int &col) {
-        Profiler profiler("FFTDataServer::getCacheWriter");
-        col = x & m_cacheWidthMask;
-        int c = x >> m_cacheWidthPower;
-        {
-            QReadLocker locker(&m_cacheVectorLock);
-            CacheBlock *cb(m_caches.at(c));
-            if (cb) {
-                if (cb->memoryCache) return cb->memoryCache;
-                if (cb->fileCacheWriter) return cb->fileCacheWriter;
-                // if cb exists, creation must have failed: don't try again
-                return 0;
-            }
-        }
-        if (!makeCache(c)) return 0;
-        return getCacheWriter(x, col);
-    }
-
-    bool haveCache(int x) {
-        int c = x >> m_cacheWidthPower;
-        return (m_caches.at(c) != 0);
-    }
-    
-    bool makeCache(int c);
-    bool makeCacheReader(int c);
-    
-    StorageAdviser::Criteria m_criteria;
-
-    void getStorageAdvice(int w, int h, bool &memory, bool &compact);
-        
-    mutable QMutex m_fftBuffersLock;
-    QWaitCondition m_condition;
-
-    fftsample *m_fftInput;
-    fftf_complex *m_fftOutput;
-    float *m_workbuffer;
-    fftf_plan m_fftPlan;
-
-    class FillThread : public Thread
-    {
-    public:
-        FillThread(FFTDataServer &server, sv_frame_t fillFromFrame) :
-            m_server(server), m_extent(0), m_completion(0),
-            m_fillFrom(fillFromFrame) { }
-
-        sv_frame_t getExtent() const { return m_extent; }
-        int getCompletion() const { return m_completion ? m_completion : 1; }
-        QString getError() const { return m_threadError; }
-        virtual void run();
-
-    protected:
-        FFTDataServer &m_server;
-        sv_frame_t m_extent;
-        int m_completion;
-        sv_frame_t m_fillFrom;
-        QString m_threadError;
-    };
-
-    bool m_exiting;
-    bool m_suspended;
-    FillThread *m_fillThread;
-    QString m_error;
-
-    void deleteProcessingData();
-    void fillColumn(int x);
-    void fillComplete();
-
-    QString generateFileBasename() const;
-    static QString generateFileBasename(const DenseTimeValueModel *model,
-                                        int channel,
-                                        WindowType windowType,
-                                        int windowSize,
-                                        int windowIncrement,
-                                        int fftSize,
-                                        bool polar);
-
-    typedef std::pair<FFTDataServer *, int> ServerCountPair;
-    typedef std::map<QString, ServerCountPair> ServerMap;
-    typedef std::deque<FFTDataServer *> ServerQueue;
-
-    static ServerMap m_servers;
-    static ServerQueue m_releasedServers; // these are still in m_servers as well, with zero refcount
-    static QMutex m_serverMapMutex;
-    static FFTDataServer *findServer(QString); // call with serverMapMutex held
-    static void purgeLimbo(int maxSize = 3); // call with serverMapMutex held
-
-    static void claimInstance(FFTDataServer *, bool needLock);
-    static void releaseInstance(FFTDataServer *, bool needLock);
-
-};
-
-#endif
--- a/data/fft/FFTFileCacheReader.cpp	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,281 +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-2009 Chris Cannam and QMUL.
-    
-    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 "FFTFileCacheReader.h"
-#include "FFTFileCacheWriter.h"
-
-#include "fileio/MatrixFile.h"
-
-#include "base/Profiler.h"
-#include "base/Thread.h"
-#include "base/Exceptions.h"
-
-#include <iostream>
-
-//#define DEBUG_FFT_FILE_CACHE_READER 1
-
-// The underlying matrix has height (m_height * 2 + 1).  In each
-// column we store magnitude at [0], [2] etc and phase at [1], [3]
-// etc, and then store the normalization factor (maximum magnitude) at
-// [m_height * 2].  In compact mode, the factor takes two cells.
-
-FFTFileCacheReader::FFTFileCacheReader(FFTFileCacheWriter *writer) :
-    m_readbuf(0),
-    m_readbufCol(0),
-    m_readbufWidth(0),
-    m_readbufGood(false),
-    m_storageType(writer->getStorageType()),
-    m_factorSize(m_storageType == FFTCache::Compact ? 2 : 1),
-    m_mfc(new MatrixFile
-          (writer->getFileBase(),
-           MatrixFile::ReadOnly,
-           int((m_storageType == FFTCache::Compact) ? sizeof(uint16_t) : sizeof(float)),
-           writer->getWidth(),
-           writer->getHeight() * 2 + m_factorSize))
-{
-#ifdef DEBUG_FFT_FILE_CACHE_READER
-    cerr << "FFTFileCacheReader: storage type is " << (m_storageType == FFTCache::Compact ? "Compact" : m_storageType == FFTCache::Polar ? "Polar" : "Rectangular") << endl;
-#endif
-}
-
-FFTFileCacheReader::~FFTFileCacheReader()
-{
-    if (m_readbuf) delete[] m_readbuf;
-    delete m_mfc;
-}
-
-int
-FFTFileCacheReader::getWidth() const
-{
-    return m_mfc->getWidth();
-}
-
-int
-FFTFileCacheReader::getHeight() const
-{
-    int mh = m_mfc->getHeight();
-    if (mh > m_factorSize) return (mh - m_factorSize) / 2;
-    else return 0;
-}
-
-float
-FFTFileCacheReader::getMagnitudeAt(int x, int y) const
-{
-    Profiler profiler("FFTFileCacheReader::getMagnitudeAt", false);
-
-    float value = 0.f;
-
-    switch (m_storageType) {
-
-    case FFTCache::Compact:
-        value = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.f)
-            * getNormalizationFactor(x);
-        break;
-
-    case FFTCache::Rectangular:
-    {
-        float real, imag;
-        getValuesAt(x, y, real, imag);
-        value = sqrtf(real * real + imag * imag);
-        break;
-    }
-
-    case FFTCache::Polar:
-        value = getFromReadBufStandard(x, y * 2);
-        break;
-    }
-
-    return value;
-}
-
-float
-FFTFileCacheReader::getNormalizedMagnitudeAt(int x, int y) const
-{
-    float value = 0.f;
-
-    switch (m_storageType) {
-
-    case FFTCache::Compact:
-        value = getFromReadBufCompactUnsigned(x, y * 2) / 65535.f;
-        break;
-
-    case FFTCache::Rectangular:
-    case FFTCache::Polar:
-    {
-        float mag = getMagnitudeAt(x, y);
-        float factor = getNormalizationFactor(x);
-        if (factor != 0) value = mag / factor;
-        else value = 0.f;
-        break;
-    }
-    }
-
-    return value;
-}
-
-float
-FFTFileCacheReader::getMaximumMagnitudeAt(int x) const
-{
-    return getNormalizationFactor(x);
-}
-
-float
-FFTFileCacheReader::getPhaseAt(int x, int y) const
-{
-    float value = 0.f;
-    
-    switch (m_storageType) {
-
-    case FFTCache::Compact:
-        value = (getFromReadBufCompactSigned(x, y * 2 + 1) / 32767.f) * float(M_PI);
-        break;
-
-    case FFTCache::Rectangular:
-    {
-        float real, imag;
-        getValuesAt(x, y, real, imag);
-        value = atan2f(imag, real);
-        break;
-    }
-
-    case FFTCache::Polar:
-        value = getFromReadBufStandard(x, y * 2 + 1);
-        break;
-    }
-
-    return value;
-}
-
-void
-FFTFileCacheReader::getValuesAt(int x, int y, float &real, float &imag) const
-{
-//    SVDEBUG << "FFTFileCacheReader::getValuesAt(" << x << "," << y << ")" << endl;
-
-    switch (m_storageType) {
-
-    case FFTCache::Rectangular:
-        real = getFromReadBufStandard(x, y * 2);
-        imag = getFromReadBufStandard(x, y * 2 + 1);
-        return;
-
-    case FFTCache::Compact:
-    case FFTCache::Polar:
-        float mag = getMagnitudeAt(x, y);
-        float phase = getPhaseAt(x, y);
-        real = mag * cosf(phase);
-        imag = mag * sinf(phase);
-        return;
-    }
-}
-
-void
-FFTFileCacheReader::getMagnitudesAt(int x, float *values, int minbin, int count, int step) const
-{
-    Profiler profiler("FFTFileCacheReader::getMagnitudesAt");
-
-    switch (m_storageType) {
-
-    case FFTCache::Compact:
-        for (int i = 0; i < count; ++i) {
-            int y = minbin + i * step;
-            values[i] = (getFromReadBufCompactUnsigned(x, y * 2) / 65535.f)
-                * getNormalizationFactor(x);
-        }
-        break;
-
-    case FFTCache::Rectangular:
-    {
-        float real, imag;
-        for (int i = 0; i < count; ++i) {
-            int y = minbin + i * step;
-            real = getFromReadBufStandard(x, y * 2);
-            imag = getFromReadBufStandard(x, y * 2 + 1);
-            values[i] = sqrtf(real * real + imag * imag);
-        }
-        break;
-    }
-
-    case FFTCache::Polar:
-        for (int i = 0; i < count; ++i) {
-            int y = minbin + i * step;
-            values[i] = getFromReadBufStandard(x, y * 2);
-        }
-        break;
-    }
-}
-
-bool
-FFTFileCacheReader::haveSetColumnAt(int x) const
-{
-    if (m_readbuf && m_readbufGood &&
-        (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) {
-//        SVDEBUG << "FFTFileCacheReader::haveSetColumnAt: short-circuiting; we know about this one" << endl;
-        return true;
-    }
-    return m_mfc->haveSetColumnAt(x);
-}
-
-size_t
-FFTFileCacheReader::getCacheSize(int width, int height,
-                                 FFTCache::StorageType type)
-{
-    return (height * 2 + (type == FFTCache::Compact ? 2 : 1)) * width *
-        (type == FFTCache::Compact ? sizeof(uint16_t) : sizeof(float)) +
-        2 * sizeof(int); // matrix file header size
-}
-
-void
-FFTFileCacheReader::populateReadBuf(int x) const
-{
-    Profiler profiler("FFTFileCacheReader::populateReadBuf", false);
-
-//    SVDEBUG << "FFTFileCacheReader::populateReadBuf(" << x << ")" << endl;
-
-    if (!m_readbuf) {
-        m_readbuf = new char[m_mfc->getHeight() * 2 * m_mfc->getCellSize()];
-    }
-
-    m_readbufGood = false;
-
-    try {
-        bool good = false;
-        if (m_mfc->haveSetColumnAt(x)) {
-            // If the column is not available, we have no obligation
-            // to do anything with the readbuf -- we can cheerfully
-            // return garbage.  It's the responsibility of the caller
-            // to check haveSetColumnAt before trusting any retrieved
-            // data.  However, we do record whether the data in the
-            // readbuf is good or not, because we can use that to
-            // return an immediate result for haveSetColumnAt if the
-            // column is right.
-            good = true;
-            m_mfc->getColumnAt(x, m_readbuf);
-        }
-        if (m_mfc->haveSetColumnAt(x + 1)) {
-            m_mfc->getColumnAt
-                (x + 1, m_readbuf + m_mfc->getCellSize() * m_mfc->getHeight());
-            m_readbufWidth = 2;
-        } else {
-            m_readbufWidth = 1;
-        }
-        m_readbufGood = good;
-    } catch (FileReadFailed f) {
-        cerr << "ERROR: FFTFileCacheReader::populateReadBuf: File read failed: "
-                  << f.what() << endl;
-        memset(m_readbuf, 0, m_mfc->getHeight() * 2 * m_mfc->getCellSize());
-    }
-    m_readbufCol = x;
-}
-
--- a/data/fft/FFTFileCacheReader.h	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +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-2009 Chris Cannam and QMUL.
-    
-    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_FILE_CACHE_READER_H_
-#define _FFT_FILE_CACHE_READER_H_
-
-#include "data/fileio/MatrixFile.h"
-#include "FFTCacheReader.h"
-#include "FFTCacheStorageType.h"
-
-class FFTFileCacheWriter;
-
-class FFTFileCacheReader : public FFTCacheReader
-{
-public:
-    FFTFileCacheReader(FFTFileCacheWriter *);
-    ~FFTFileCacheReader();
-
-    int getWidth() const;
-    int getHeight() const;
-	
-    float getMagnitudeAt(int x, int y) const;
-    float getNormalizedMagnitudeAt(int x, int y) const;
-    float getMaximumMagnitudeAt(int x) const;
-    float getPhaseAt(int x, int y) const;
-
-    void getValuesAt(int x, int y, float &real, float &imag) const;
-    void getMagnitudesAt(int x, float *values, int minbin, int count, int step) const;
-
-    bool haveSetColumnAt(int x) const;
-
-    static size_t getCacheSize(int width, int height,
-                               FFTCache::StorageType type);
-
-    FFTCache::StorageType getStorageType() const { return m_storageType; }
-
-protected:
-    mutable char *m_readbuf;
-    mutable int m_readbufCol;
-    mutable int m_readbufWidth;
-    mutable bool m_readbufGood;
-
-    float getFromReadBufStandard(int x, int y) const {
-        float v;
-        if (m_readbuf &&
-            (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) {
-            v = ((float *)m_readbuf)[(x - m_readbufCol) * m_mfc->getHeight() + y];
-            return v;
-        } else {
-            populateReadBuf(x);
-            v = getFromReadBufStandard(x, y);
-            return v;
-        }
-    }
-
-    float getFromReadBufCompactUnsigned(int x, int y) const {
-        float v;
-        if (m_readbuf &&
-            (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) {
-            v = ((uint16_t *)m_readbuf)[(x - m_readbufCol) * m_mfc->getHeight() + y];
-            return v;
-        } else {
-            populateReadBuf(x);
-            v = getFromReadBufCompactUnsigned(x, y);
-            return v;
-        }
-    }
-
-    float getFromReadBufCompactSigned(int x, int y) const {
-        float v;
-        if (m_readbuf &&
-            (m_readbufCol == x || (m_readbufWidth > 1 && m_readbufCol+1 == x))) {
-            v = ((int16_t *)m_readbuf)[(x - m_readbufCol) * m_mfc->getHeight() + y];
-            return v;
-        } else {
-            populateReadBuf(x);
-            v = getFromReadBufCompactSigned(x, y);
-            return v;
-        }
-    }
-
-    void populateReadBuf(int x) const;
-
-    float getNormalizationFactor(int col) const {
-        int h = m_mfc->getHeight();
-        if (h < m_factorSize) return 0;
-        if (m_storageType != FFTCache::Compact) {
-            return getFromReadBufStandard(col, h - 1);
-        } else {
-            union {
-                float f;
-                uint16_t u[2];
-            } factor;
-            if (!m_readbuf ||
-                !(m_readbufCol == col ||
-                  (m_readbufWidth > 1 && m_readbufCol+1 == col))) {
-                populateReadBuf(col);
-            }
-            int ix = (col - m_readbufCol) * m_mfc->getHeight() + h;
-            factor.u[0] = ((uint16_t *)m_readbuf)[ix - 2];
-            factor.u[1] = ((uint16_t *)m_readbuf)[ix - 1];
-            return factor.f;
-        }
-    }
- 
-    FFTCache::StorageType m_storageType;
-    int m_factorSize;
-    MatrixFile *m_mfc;
-};
-
-#endif
--- a/data/fft/FFTFileCacheWriter.cpp	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,195 +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-2009 Chris Cannam and QMUL.
-    
-    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 "FFTFileCacheWriter.h"
-
-#include "fileio/MatrixFile.h"
-
-#include "base/Profiler.h"
-#include "base/Thread.h"
-#include "base/Exceptions.h"
-
-#include <iostream>
-
-//#define DEBUG_FFT_FILE_CACHE_WRITER 1
-
-
-// The underlying matrix has height (m_height * 2 + 1).  In each
-// column we store magnitude at [0], [2] etc and phase at [1], [3]
-// etc, and then store the normalization factor (maximum magnitude) at
-// [m_height * 2].  In compact mode, the factor takes two cells.
-
-FFTFileCacheWriter::FFTFileCacheWriter(QString fileBase,
-                                       FFTCache::StorageType storageType,
-                                       int width, int height) :
-    m_writebuf(0),
-    m_fileBase(fileBase),
-    m_storageType(storageType),
-    m_factorSize(storageType == FFTCache::Compact ? 2 : 1),
-    m_mfc(new MatrixFile
-          (fileBase, MatrixFile::WriteOnly, 
-           int((storageType == FFTCache::Compact) ? sizeof(uint16_t) : sizeof(float)),
-           width, height * 2 + m_factorSize))
-{
-#ifdef DEBUG_FFT_FILE_CACHE_WRITER
-    cerr << "FFTFileCacheWriter: storage type is " << (storageType == FFTCache::Compact ? "Compact" : storageType == FFTCache::Polar ? "Polar" : "Rectangular") << ", size " << width << "x" << height << endl;
-#endif
-    m_mfc->setAutoClose(true);
-    m_writebuf = new char[(height * 2 + m_factorSize) * m_mfc->getCellSize()];
-}
-
-FFTFileCacheWriter::~FFTFileCacheWriter()
-{
-    if (m_writebuf) delete[] m_writebuf;
-    delete m_mfc;
-}
-
-QString
-FFTFileCacheWriter::getFileBase() const
-{
-    return m_fileBase;
-}
-
-int
-FFTFileCacheWriter::getWidth() const
-{
-    return m_mfc->getWidth();
-}
-
-int
-FFTFileCacheWriter::getHeight() const
-{
-    int mh = m_mfc->getHeight();
-    if (mh > m_factorSize) return (mh - m_factorSize) / 2;
-    else return 0;
-}
-
-bool
-FFTFileCacheWriter::haveSetColumnAt(int x) const
-{
-    return m_mfc->haveSetColumnAt(x);
-}
-
-void
-FFTFileCacheWriter::setColumnAt(int x, float *mags, float *phases, float factor)
-{
-    int h = getHeight();
-
-    switch (m_storageType) {
-
-    case FFTCache::Compact:
-        for (int y = 0; y < h; ++y) {
-            ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mags[y] / factor) * 65535.0);
-            ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phases[y] * 32767) / M_PI));
-        }
-        break;
-
-    case FFTCache::Rectangular:
-        for (int y = 0; y < h; ++y) {
-            ((float *)m_writebuf)[y * 2] = mags[y] * cosf(phases[y]);
-            ((float *)m_writebuf)[y * 2 + 1] = mags[y] * sinf(phases[y]);
-        }
-        break;
-
-    case FFTCache::Polar:
-        for (int y = 0; y < h; ++y) {
-            ((float *)m_writebuf)[y * 2] = mags[y];
-            ((float *)m_writebuf)[y * 2 + 1] = phases[y];
-        }
-        break;
-    }
-
-    static float maxFactor = 0;
-    if (factor > maxFactor) maxFactor = factor;
-#ifdef DEBUG_FFT_FILE_CACHE_WRITER
-    cerr << "Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << endl;
-#endif
-
-    setNormalizationFactorToWritebuf(factor);
-
-    m_mfc->setColumnAt(x, m_writebuf);
-}
-
-void
-FFTFileCacheWriter::setColumnAt(int x, float *real, float *imag)
-{
-    int h = getHeight();
-
-    float factor = 0.0f;
-
-    switch (m_storageType) {
-
-    case FFTCache::Compact:
-        for (int y = 0; y < h; ++y) {
-            float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
-            if (mag > factor) factor = mag;
-        }
-        for (int y = 0; y < h; ++y) {
-            float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
-            float phase = atan2f(imag[y], real[y]);
-            ((uint16_t *)m_writebuf)[y * 2] = uint16_t((mag / factor) * 65535.0);
-            ((uint16_t *)m_writebuf)[y * 2 + 1] = uint16_t(int16_t((phase * 32767) / M_PI));
-        }
-        break;
-
-    case FFTCache::Rectangular:
-        for (int y = 0; y < h; ++y) {
-            ((float *)m_writebuf)[y * 2] = real[y];
-            ((float *)m_writebuf)[y * 2 + 1] = imag[y];
-            float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
-            if (mag > factor) factor = mag;
-        }
-        break;
-
-    case FFTCache::Polar:
-        for (int y = 0; y < h; ++y) {
-            float mag = sqrtf(real[y] * real[y] + imag[y] * imag[y]);
-            if (mag > factor) factor = mag;
-            ((float *)m_writebuf)[y * 2] = mag;
-            float phase = atan2f(imag[y], real[y]);
-            ((float *)m_writebuf)[y * 2 + 1] = phase;
-        }
-        break;
-    }
-
-    static float maxFactor = 0;
-    if (factor > maxFactor) maxFactor = factor;
-#ifdef DEBUG_FFT_FILE_CACHE_WRITER
-    cerr << "[RI] Column " << x << ": normalization factor: " << factor << ", max " << maxFactor << " (height " << getHeight() << ")" << endl;
-#endif
-
-    setNormalizationFactorToWritebuf(factor);
-
-    m_mfc->setColumnAt(x, m_writebuf);
-}
-
-size_t
-FFTFileCacheWriter::getCacheSize(int width, int height,
-                                 FFTCache::StorageType type)
-{
-    return (height * 2 + (type == FFTCache::Compact ? 2 : 1)) * width *
-        (type == FFTCache::Compact ? sizeof(uint16_t) : sizeof(float)) +
-        2 * sizeof(int); // matrix file header size
-}
-
-void
-FFTFileCacheWriter::allColumnsWritten()
-{
-#ifdef DEBUG_FFT_FILE_CACHE_WRITER
-    SVDEBUG << "FFTFileCacheWriter::allColumnsWritten" << endl;
-#endif
-    m_mfc->close();
-}
-
--- a/data/fft/FFTFileCacheWriter.h	Fri Jun 12 13:46:44 2015 +0100
+++ /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-2009 Chris Cannam and QMUL.
-    
-    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_FILE_CACHE_WRITER_H_
-#define _FFT_FILE_CACHE_WRITER_H_
-
-#include "FFTCacheStorageType.h"
-#include "FFTCacheWriter.h"
-#include "data/fileio/MatrixFile.h"
-
-class FFTFileCacheWriter : public FFTCacheWriter
-{
-public:
-    FFTFileCacheWriter(QString fileBase,
-                       FFTCache::StorageType storageType,
-                       int width, int height);
-    ~FFTFileCacheWriter();
-
-    int getWidth() const;
-    int getHeight() const;
-
-    void setColumnAt(int x, float *mags, float *phases, float factor);
-    void setColumnAt(int x, float *reals, float *imags);
-
-    static size_t getCacheSize(int width, int height,
-                               FFTCache::StorageType type);
-
-    bool haveSetColumnAt(int x) const;
-
-    void allColumnsWritten();
-
-    QString getFileBase() const;
-    FFTCache::StorageType getStorageType() const { return m_storageType; }
-
-protected:
-    char *m_writebuf;
-
-    void setNormalizationFactorToWritebuf(float newfactor) {
-        int h = m_mfc->getHeight();
-        if (h < m_factorSize) return;
-        if (m_storageType != FFTCache::Compact) {
-            ((float *)m_writebuf)[h - 1] = newfactor;
-        } else {
-            union {
-                float f;
-                uint16_t u[2];
-            } factor;
-            factor.f = newfactor;
-            ((uint16_t *)m_writebuf)[h - 2] = factor.u[0];
-            ((uint16_t *)m_writebuf)[h - 1] = factor.u[1];
-        }
-    }            
-
-    QString m_fileBase;
-    FFTCache::StorageType m_storageType;
-    int m_factorSize;
-    MatrixFile *m_mfc;
-};
-
-#endif
--- a/data/fft/FFTMemoryCache.cpp	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,218 +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 "FFTMemoryCache.h"
-#include "system/System.h"
-
-#include <iostream>
-#include <cstdlib>
-
-//#define DEBUG_FFT_MEMORY_CACHE 1
-
-FFTMemoryCache::FFTMemoryCache(FFTCache::StorageType storageType,
-                               int width, int height) :
-    m_width(width),
-    m_height(height),
-    m_magnitude(0),
-    m_phase(0),
-    m_fmagnitude(0),
-    m_fphase(0),
-    m_freal(0),
-    m_fimag(0),
-    m_factor(0),
-    m_storageType(storageType)
-{
-#ifdef DEBUG_FFT_MEMORY_CACHE
-    cerr << "FFTMemoryCache[" << this << "]::FFTMemoryCache (type "
-              << m_storageType << "), size " << m_width << "x" << m_height << endl;
-#endif
-
-    initialise();
-}
-
-FFTMemoryCache::~FFTMemoryCache()
-{
-#ifdef DEBUG_FFT_MEMORY_CACHE
-    cerr << "FFTMemoryCache[" << this << "]::~FFTMemoryCache" << endl;
-#endif
-
-    for (int 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_fmagnitude && m_fmagnitude[i]) free(m_fmagnitude[i]);
-	if (m_fphase && m_fphase[i]) free(m_fphase[i]);
-        if (m_freal && m_freal[i]) free(m_freal[i]);
-        if (m_fimag && m_fimag[i]) free(m_fimag[i]);
-    }
-
-    if (m_magnitude) free(m_magnitude);
-    if (m_phase) free(m_phase);
-    if (m_fmagnitude) free(m_fmagnitude);
-    if (m_fphase) free(m_fphase);
-    if (m_freal) free(m_freal);
-    if (m_fimag) free(m_fimag);
-    if (m_factor) free(m_factor);
-}
-
-void
-FFTMemoryCache::initialise()
-{
-    Profiler profiler("FFTMemoryCache::initialise");
-
-    int width = m_width, height = m_height;
-
-#ifdef DEBUG_FFT_MEMORY_CACHE
-    cerr << "FFTMemoryCache[" << this << "]::initialise(" << width << "x" << height << " = " << width*height << ")" << endl;
-#endif
-
-    if (m_storageType == FFTCache::Compact) {
-        initialise(m_magnitude);
-        initialise(m_phase);
-    } else if (m_storageType == FFTCache::Polar) {
-        initialise(m_fmagnitude);
-        initialise(m_fphase);
-    } else {
-        initialise(m_freal);
-        initialise(m_fimag);
-    }
-
-    m_colset.resize(width);
-
-    m_factor = (float *)realloc(m_factor, width * sizeof(float));
-
-    m_width = width;
-    m_height = height;
-
-#ifdef DEBUG_FFT_MEMORY_CACHE
-    cerr << "done, width = " << m_width << " height = " << m_height << endl;
-#endif
-}
-
-void
-FFTMemoryCache::initialise(uint16_t **&array)
-{
-    array = (uint16_t **)malloc(m_width * sizeof(uint16_t *));
-    if (!array) throw std::bad_alloc();
-    MUNLOCK(array, m_width * sizeof(uint16_t *));
-
-    for (int i = 0; i < m_width; ++i) {
-	array[i] = (uint16_t *)malloc(m_height * sizeof(uint16_t));
-	if (!array[i]) throw std::bad_alloc();
-	MUNLOCK(array[i], m_height * sizeof(uint16_t));
-    }
-}
-
-void
-FFTMemoryCache::initialise(float **&array)
-{
-    array = (float **)malloc(m_width * sizeof(float *));
-    if (!array) throw std::bad_alloc();
-    MUNLOCK(array, m_width * sizeof(float *));
-
-    for (int i = 0; i < m_width; ++i) {
-	array[i] = (float *)malloc(m_height * sizeof(float));
-	if (!array[i]) throw std::bad_alloc();
-	MUNLOCK(array[i], m_height * sizeof(float));
-    }
-}
-
-void
-FFTMemoryCache::setColumnAt(int x, float *mags, float *phases, float factor)
-{
-    Profiler profiler("FFTMemoryCache::setColumnAt: from polar");
-
-    setNormalizationFactor(x, factor);
-
-    if (m_storageType == FFTCache::Rectangular) {
-        Profiler subprof("FFTMemoryCache::setColumnAt: polar to cart");
-        for (int y = 0; y < m_height; ++y) {
-            m_freal[x][y] = mags[y] * cosf(phases[y]);
-            m_fimag[x][y] = mags[y] * sinf(phases[y]);
-        }
-    } else {
-        for (int y = 0; y < m_height; ++y) {
-            setMagnitudeAt(x, y, mags[y]);
-            setPhaseAt(x, y, phases[y]);
-        }
-    }
-
-    m_colsetLock.lockForWrite();
-    m_colset.set(x);
-    m_colsetLock.unlock();
-}
-
-void
-FFTMemoryCache::setColumnAt(int x, float *reals, float *imags)
-{
-    Profiler profiler("FFTMemoryCache::setColumnAt: from cart");
-
-    float max = 0.0;
-
-    switch (m_storageType) {
-
-    case FFTCache::Rectangular:
-        for (int y = 0; y < m_height; ++y) {
-            m_freal[x][y] = reals[y];
-            m_fimag[x][y] = imags[y];
-            float mag = sqrtf(reals[y] * reals[y] + imags[y] * imags[y]);
-            if (mag > max) max = mag;
-        }
-        break;
-
-    case FFTCache::Compact:
-    case FFTCache::Polar:
-    {
-        Profiler subprof("FFTMemoryCache::setColumnAt: cart to polar");
-        for (int y = 0; y < m_height; ++y) {
-            float mag = sqrtf(reals[y] * reals[y] + imags[y] * imags[y]);
-            float phase = atan2f(imags[y], reals[y]);
-            reals[y] = mag;
-            imags[y] = phase;
-            if (mag > max) max = mag;
-        }
-        break;
-    }
-    };
-
-    if (m_storageType == FFTCache::Rectangular) {
-        m_factor[x] = max;
-        m_colsetLock.lockForWrite();
-        m_colset.set(x);
-        m_colsetLock.unlock();
-    } else {
-        setColumnAt(x, reals, imags, max);
-    }
-}
-
-size_t
-FFTMemoryCache::getCacheSize(int width, int height, FFTCache::StorageType type)
-{
-    size_t sz = 0;
-
-    switch (type) {
-
-    case FFTCache::Compact:
-        sz = (height * 2 + 1) * width * sizeof(uint16_t);
-        break;
-
-    case FFTCache::Polar:
-    case FFTCache::Rectangular:
-        sz = (height * 2 + 1) * width * sizeof(float);
-        break;
-    }
-
-    return sz;
-}
-
--- a/data/fft/FFTMemoryCache.h	Fri Jun 12 13:46:44 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +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_MEMORY_CACHE_H_
-#define _FFT_MEMORY_CACHE_H_
-
-#include "FFTCacheReader.h"
-#include "FFTCacheWriter.h"
-#include "FFTCacheStorageType.h"
-#include "base/ResizeableBitset.h"
-#include "base/Profiler.h"
-
-#include <QReadWriteLock>
-
-/**
- * In-memory FFT cache.  For this we want 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 FFTCacheReader, public FFTCacheWriter
-{
-public:
-    FFTMemoryCache(FFTCache::StorageType storageType,
-                   int width, int height);
-    ~FFTMemoryCache();
-	
-    int getWidth() const { return m_width; }
-    int getHeight() const { return m_height; }
-	
-    float getMagnitudeAt(int x, int y) const {
-        if (m_storageType == FFTCache::Rectangular) {
-            Profiler profiler("FFTMemoryCache::getMagnitudeAt: cart to polar");
-            return sqrtf(m_freal[x][y] * m_freal[x][y] +
-                         m_fimag[x][y] * m_fimag[x][y]);
-        } else {
-            return getNormalizedMagnitudeAt(x, y) * m_factor[x];
-        }
-    }
-    
-    float getNormalizedMagnitudeAt(int x, int y) const {
-        if (m_storageType == FFTCache::Rectangular) return getMagnitudeAt(x, y) / m_factor[x];
-        else if (m_storageType == FFTCache::Polar) return m_fmagnitude[x][y];
-        else return float(m_magnitude[x][y]) / 65535.f;
-    }
-    
-    float getMaximumMagnitudeAt(int x) const {
-        return m_factor[x];
-    }
-    
-    float getPhaseAt(int x, int y) const {
-        if (m_storageType == FFTCache::Rectangular) {
-            Profiler profiler("FFTMemoryCache::getValuesAt: cart to polar");
-            return atan2f(m_fimag[x][y], m_freal[x][y]);
-        } else if (m_storageType == FFTCache::Polar) {
-            return m_fphase[x][y];
-        } else {
-            int16_t i = (int16_t)m_phase[x][y];
-            return float(i / 32767.0 * M_PI);
-        }
-    }
-    
-    void getValuesAt(int x, int y, float &real, float &imag) const {
-        if (m_storageType == FFTCache::Rectangular) {
-            real = m_freal[x][y];
-            imag = m_fimag[x][y];
-        } else {
-            Profiler profiler("FFTMemoryCache::getValuesAt: polar to cart");
-            float mag = getMagnitudeAt(x, y);
-            float phase = getPhaseAt(x, y);
-            real = mag * cosf(phase);
-            imag = mag * sinf(phase);
-        }
-    }
-
-    void getMagnitudesAt(int x, float *values, int minbin, int count, int step) const
-    {
-        if (m_storageType == FFTCache::Rectangular) {
-            for (int i = 0; i < count; ++i) {
-                int y = i * step + minbin;
-                values[i] = sqrtf(m_freal[x][y] * m_freal[x][y] +
-                                  m_fimag[x][y] * m_fimag[x][y]);
-            }
-        } else if (m_storageType == FFTCache::Polar) {
-            for (int i = 0; i < count; ++i) {
-                int y = i * step + minbin;
-                values[i] = m_fmagnitude[x][y] * m_factor[x];
-            }
-        } else {
-            for (int i = 0; i < count; ++i) {
-                int y = i * step + minbin;
-                values[i] = float(double(m_magnitude[x][y]) * m_factor[x] / 65535.0);
-            }
-        }
-    }
-
-    bool haveSetColumnAt(int x) const {
-        m_colsetLock.lockForRead();
-        bool have = m_colset.get(x);
-        m_colsetLock.unlock();
-        return have;
-    }
-
-    void setColumnAt(int x, float *mags, float *phases, float factor);
-
-    void setColumnAt(int x, float *reals, float *imags);
-
-    void allColumnsWritten() { } 
-
-    static size_t getCacheSize(int width, int height,
-                               FFTCache::StorageType type);
-
-    FFTCache::StorageType getStorageType() const { return m_storageType; }
-
-private:
-    int m_width;
-    int m_height;
-    uint16_t **m_magnitude;
-    uint16_t **m_phase;
-    float **m_fmagnitude;
-    float **m_fphase;
-    float **m_freal;
-    float **m_fimag;
-    float *m_factor;
-    FFTCache::StorageType m_storageType;
-    ResizeableBitset m_colset;
-    mutable QReadWriteLock m_colsetLock;
-
-    void initialise();
-
-    void setNormalizationFactor(int x, float factor) {
-        if (x < m_width) m_factor[x] = factor;
-    }
-    
-    void setMagnitudeAt(int x, int y, float mag) {
-         // norm factor must already be set
-        setNormalizedMagnitudeAt(x, y, mag / m_factor[x]);
-    }
-    
-    void setNormalizedMagnitudeAt(int x, int y, float norm) {
-        if (x < m_width && y < m_height) {
-            if (m_storageType == FFTCache::Polar) m_fmagnitude[x][y] = norm;
-            else m_magnitude[x][y] = uint16_t(norm * 65535.0);
-        }
-    }
-    
-    void setPhaseAt(int x, int y, float phase) {
-        // phase in range -pi -> pi
-        if (x < m_width && y < m_height) {
-            if (m_storageType == FFTCache::Polar) m_fphase[x][y] = phase;
-            else m_phase[x][y] = uint16_t(int16_t((phase * 32767) / M_PI));
-        }
-    }
-
-    void initialise(uint16_t **&);
-    void initialise(float **&);
-};
-
-
-#endif
-
--- a/data/model/FFTModel.cpp	Fri Jun 12 13:46:44 2015 +0100
+++ b/data/model/FFTModel.cpp	Fri Jun 12 14:51:46 2015 +0100
@@ -15,7 +15,6 @@
 
 #include "FFTModel.h"
 #include "DenseTimeValueModel.h"
-#include "AggregateWaveModel.h"
 
 #include "base/Profiler.h"
 #include "base/Pitch.h"
@@ -23,178 +22,43 @@
 #include <algorithm>
 
 #include <cassert>
+#include <deque>
 
 #ifndef __GNUC__
 #include <alloca.h>
 #endif
 
+using namespace std;
+
 FFTModel::FFTModel(const DenseTimeValueModel *model,
                    int channel,
                    WindowType windowType,
                    int windowSize,
                    int windowIncrement,
-                   int fftSize,
-                   bool polar,
-                   StorageAdviser::Criteria criteria,
-                   sv_frame_t fillFromFrame) :
-    //!!! ZoomConstraint!
-    m_server(0),
-    m_xshift(0),
-    m_yshift(0)
+                   int fftSize) :
+    m_model(model),
+    m_channel(channel),
+    m_windowType(windowType),
+    m_windowSize(windowSize),
+    m_windowIncrement(windowIncrement),
+    m_fftSize(fftSize),
+    m_windower(windowType, windowSize)
 {
-    setSourceModel(const_cast<DenseTimeValueModel *>(model)); //!!! hmm.
-
-    m_server = getServer(model,
-                         channel,
-                         windowType,
-                         windowSize,
-                         windowIncrement,
-                         fftSize,
-                         polar,
-                         criteria,
-                         fillFromFrame);
-
-    if (!m_server) return; // caller should check isOK()
-
-    int xratio = windowIncrement / m_server->getWindowIncrement();
-    int yratio = m_server->getFFTSize() / fftSize;
-
-    while (xratio > 1) {
-        if (xratio & 0x1) {
-            cerr << "ERROR: FFTModel: Window increment ratio "
-                      << windowIncrement << " / "
-                      << m_server->getWindowIncrement()
-                      << " must be a power of two" << endl;
-            assert(!(xratio & 0x1));
-        }
-        ++m_xshift;
-        xratio >>= 1;
-    }
-
-    while (yratio > 1) {
-        if (yratio & 0x1) {
-            cerr << "ERROR: FFTModel: FFT size ratio "
-                      << m_server->getFFTSize() << " / " << fftSize
-                      << " must be a power of two" << endl;
-            assert(!(yratio & 0x1));
-        }
-        ++m_yshift;
-        yratio >>= 1;
-    }
 }
 
 FFTModel::~FFTModel()
 {
-    if (m_server) FFTDataServer::releaseInstance(m_server);
 }
 
 void
 FFTModel::sourceModelAboutToBeDeleted()
 {
-    if (m_sourceModel) {
-        cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_sourceModel << ")" << endl;
-        if (m_server) {
-            FFTDataServer::releaseInstance(m_server);
-            m_server = 0;
-        }
-        FFTDataServer::modelAboutToBeDeleted(m_sourceModel);
+    if (m_model) {
+        cerr << "FFTModel[" << this << "]::sourceModelAboutToBeDeleted(" << m_model << ")" << endl;
+        m_model = 0;
     }
 }
 
-FFTDataServer *
-FFTModel::getServer(const DenseTimeValueModel *model,
-                    int channel,
-                    WindowType windowType,
-                    int windowSize,
-                    int windowIncrement,
-                    int fftSize,
-                    bool polar,
-                    StorageAdviser::Criteria criteria,
-                    sv_frame_t fillFromFrame)
-{
-    // Obviously, an FFT model of channel C (where C != -1) of an
-    // aggregate model is the same as the FFT model of the appropriate
-    // channel of whichever model that aggregate channel is drawn
-    // from.  We should use that model here, in case we already have
-    // the data for it or will be wanting the same data again later.
-
-    // If the channel is -1 (i.e. mixture of all channels), then we
-    // can't do this shortcut unless the aggregate model only has one
-    // channel or contains exactly all of the channels of a single
-    // other model.  That isn't very likely -- if it were the case,
-    // why would we be using an aggregate model?
-
-    if (channel >= 0) {
-
-        const AggregateWaveModel *aggregate =
-            dynamic_cast<const AggregateWaveModel *>(model);
-
-        if (aggregate && channel < aggregate->getComponentCount()) {
-
-            AggregateWaveModel::ModelChannelSpec spec =
-                aggregate->getComponent(channel);
-
-            return getServer(spec.model,
-                             spec.channel,
-                             windowType,
-                             windowSize,
-                             windowIncrement,
-                             fftSize,
-                             polar,
-                             criteria,
-                             fillFromFrame);
-        }
-    }
-
-    // The normal case
-
-    return FFTDataServer::getFuzzyInstance(model,
-                                           channel,
-                                           windowType,
-                                           windowSize,
-                                           windowIncrement,
-                                           fftSize,
-                                           polar,
-                                           criteria,
-                                           fillFromFrame);
-}
-
-sv_samplerate_t
-FFTModel::getSampleRate() const
-{
-    return isOK() ? m_server->getModel()->getSampleRate() : 0;
-}
-
-FFTModel::Column
-FFTModel::getColumn(int x) const
-{
-    Profiler profiler("FFTModel::getColumn", false);
-
-    Column result;
-
-    result.clear();
-    int h = getHeight();
-    result.reserve(h);
-
-#ifdef __GNUC__
-    float magnitudes[h];
-#else
-    float *magnitudes = (float *)alloca(h * sizeof(float));
-#endif
-
-    if (m_server->getMagnitudesAt(x << m_xshift, magnitudes)) {
-
-        for (int y = 0; y < h; ++y) {
-            result.push_back(magnitudes[y]);
-        }
-
-    } else {
-        for (int i = 0; i < h; ++i) result.push_back(0.f);
-    }
-
-    return result;
-}
-
 QString
 FFTModel::getBinName(int n) const
 {
@@ -209,10 +73,7 @@
 {
     if (!isOK()) return false;
 
-    sv_samplerate_t sampleRate = m_server->getModel()->getSampleRate();
-
-    int fftSize = m_server->getFFTSize() >> m_yshift;
-    frequency = double(y * sampleRate) / fftSize;
+    frequency = double(y * getSampleRate()) / m_fftSize;
 
     if (x+1 >= getWidth()) return false;
 
@@ -230,17 +91,15 @@
 
     int incr = getResolution();
 
-    double expectedPhase = oldPhase + (2.0 * M_PI * y * incr) / fftSize;
+    double expectedPhase = oldPhase + (2.0 * M_PI * y * incr) / m_fftSize;
 
     double phaseError = princarg(newPhase - expectedPhase);
 
-//    bool stable = (fabsf(phaseError) < (1.1f * (m_windowIncrement * M_PI) / m_fftSize));
-
     // The new frequency estimate based on the phase error resulting
     // from assuming the "native" frequency of this bin
 
     frequency =
-        (sampleRate * (expectedPhase + phaseError - oldPhase)) /
+        (getSampleRate() * (expectedPhase + phaseError - oldPhase)) /
         (2.0 * M_PI * incr);
 
     return true;
@@ -293,8 +152,8 @@
 
     sv_samplerate_t sampleRate = getSampleRate();
 
-    std::deque<float> window;
-    std::vector<int> inrange;
+    deque<float> window;
+    vector<int> inrange;
     float dist = 0.5;
 
     int medianWinSize = getPeakPickWindowSize(type, sampleRate, ymin, dist);
@@ -331,8 +190,8 @@
             else binmax = values.size()-1;
         }
 
-        std::deque<float> sorted(window);
-        std::sort(sorted.begin(), sorted.end());
+        deque<float> sorted(window);
+        sort(sorted.begin(), sorted.end());
         float median = sorted[int(float(sorted.size()) * dist)];
 
         int centrebin = 0;
@@ -380,15 +239,14 @@
     if (type == MajorPeaks) return 10;
     if (bin == 0) return 3;
 
-    int fftSize = m_server->getFFTSize() >> m_yshift;
-    double binfreq = (sampleRate * bin) / fftSize;
+    double binfreq = (getSampleRate() * bin) / m_fftSize;
     double hifreq = Pitch::getFrequencyForPitch(73, 0, binfreq);
 
-    int hibin = int(lrint((hifreq * fftSize) / sampleRate));
+    int hibin = int(lrint((hifreq * m_fftSize) / getSampleRate()));
     int medianWinSize = hibin - bin;
     if (medianWinSize < 3) medianWinSize = 3;
 
-    percentile = 0.5f + float(binfreq / sampleRate);
+    percentile = 0.5f + float(binfreq / getSampleRate());
 
     return medianWinSize;
 }
@@ -404,7 +262,6 @@
     PeakLocationSet locations = getPeaks(type, x, ymin, ymax);
 
     sv_samplerate_t sampleRate = getSampleRate();
-    int fftSize = m_server->getFFTSize() >> m_yshift;
     int incr = getResolution();
 
     // This duplicates some of the work of estimateStableFrequency to
@@ -412,7 +269,7 @@
     // columns, instead of jumping back and forth between columns x and
     // x+1, which may be significantly slower if re-seeking is needed
 
-    std::vector<float> phases;
+    vector<float> phases;
     for (PeakLocationSet::iterator i = locations.begin();
          i != locations.end(); ++i) {
         phases.push_back(getPhaseAt(x, *i));
@@ -423,13 +280,11 @@
          i != locations.end(); ++i) {
         double oldPhase = phases[phaseIndex];
         double newPhase = getPhaseAt(x+1, *i);
-        double expectedPhase = oldPhase + (2.0 * M_PI * *i * incr) / fftSize;
+        double expectedPhase = oldPhase + (2.0 * M_PI * *i * incr) / m_fftSize;
         double phaseError = princarg(newPhase - expectedPhase);
         double frequency =
             (sampleRate * (expectedPhase + phaseError - oldPhase))
             / (2 * M_PI * incr);
-//        bool stable = (fabsf(phaseError) < (1.1f * (incr * M_PI) / fftSize));
-//        if (stable)
         peaks[*i] = frequency;
         ++phaseIndex;
     }
@@ -437,12 +292,3 @@
     return peaks;
 }
 
-FFTModel::FFTModel(const FFTModel &model) :
-    DenseThreeDimensionalModel(),
-    m_server(model.m_server),
-    m_xshift(model.m_xshift),
-    m_yshift(model.m_yshift)
-{
-    FFTDataServer::claimInstance(m_server);
-}
-
--- a/data/model/FFTModel.h	Fri Jun 12 13:46:44 2015 +0100
+++ b/data/model/FFTModel.h	Fri Jun 12 14:51:46 2015 +0100
@@ -16,8 +16,10 @@
 #ifndef FFT_MODEL_H
 #define FFT_MODEL_H
 
-#include "data/fft/FFTDataServer.h"
 #include "DenseThreeDimensionalModel.h"
+#include "DenseTimeValueModel.h"
+
+#include "base/Window.h"
 
 #include <set>
 #include <map>
@@ -25,11 +27,8 @@
 /**
  * An implementation of DenseThreeDimensionalModel that makes FFT data
  * derived from a DenseTimeValueModel available as a generic data
- * grid.  The FFT data is acquired using FFTDataServer.  Note that any
- * of the accessor functions may throw AllocationFailed if a cache
- * resize fails.
+ * grid.
  */
-
 class FFTModel : public DenseThreeDimensionalModel
 {
     Q_OBJECT
@@ -43,111 +42,61 @@
      * If the model has multiple channels use only the given channel,
      * unless the channel is -1 in which case merge all available
      * channels.
-     * 
-     * If polar is true, the data will normally be retrieved from the
-     * FFT model in magnitude/phase form; otherwise it will normally
-     * be retrieved in "cartesian" real/imaginary form.  The results
-     * should be the same either way, but a "polar" model addressed in
-     * "cartesian" form or vice versa may suffer a performance
-     * penalty.
-     *
-     * The fillFromColumn argument gives a hint that the FFT data
-     * server should aim to start calculating FFT data at that column
-     * number if possible, as that is likely to be requested first.
      */
     FFTModel(const DenseTimeValueModel *model,
              int channel,
              WindowType windowType,
              int windowSize,
              int windowIncrement,
-             int fftSize,
-             bool polar,
-             StorageAdviser::Criteria criteria = StorageAdviser::NoCriteria,
-             sv_frame_t fillFromFrame = 0);
+             int fftSize);
     ~FFTModel();
 
-    inline float getMagnitudeAt(int x, int y) {
-        return m_server->getMagnitudeAt(x << m_xshift, y << m_yshift);
-    }
-    inline float getNormalizedMagnitudeAt(int x, int y) {
-        return m_server->getNormalizedMagnitudeAt(x << m_xshift, y << m_yshift);
-    }
-    inline float getMaximumMagnitudeAt(int x) {
-        return m_server->getMaximumMagnitudeAt(x << m_xshift);
-    }
-    inline float getPhaseAt(int x, int y) {
-        return m_server->getPhaseAt(x << m_xshift, y << m_yshift);
-    }
-    inline void getValuesAt(int x, int y, float &real, float &imaginary) {
-        m_server->getValuesAt(x << m_xshift, y << m_yshift, real, imaginary);
-    }
-    inline bool isColumnAvailable(int x) const {
-        return m_server->isColumnReady(x << m_xshift);
-    }
-
-    inline bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) {
-        return m_server->getMagnitudesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio());
-    }
-    inline bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) {
-        return m_server->getNormalizedMagnitudesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio());
-    }
-    inline bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) {
-        return m_server->getPhasesAt(x << m_xshift, values, minbin << m_yshift, count, getYRatio());
-    }
-    inline bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) {
-        return m_server->getValuesAt(x << m_xshift, reals, imaginaries, minbin << m_yshift, count, getYRatio());
-    }
-
-    inline sv_frame_t getFillExtent() const { return m_server->getFillExtent(); }
-
     // DenseThreeDimensionalModel and Model methods:
     //
-    inline virtual int getWidth() const {
-        return m_server->getWidth() >> m_xshift;
-    }
-    inline virtual int getHeight() const {
-        // If there is no y-shift, the server's height (based on its
-        // fftsize/2 + 1) is correct.  If there is a shift, then the
-        // server is using a larger fft size than we want, so we shift
-        // it right as many times as necessary, but then we need to
-        // re-add the "+1" part (because ((fftsize*2)/2 + 1) / 2 !=
-        // fftsize/2 + 1).
-        return (m_server->getHeight() >> m_yshift) + (m_yshift > 0 ? 1 : 0);
-    }
-    virtual float getValueAt(int x, int y) const {
-        return const_cast<FFTModel *>(this)->getMagnitudeAt(x, y);
-    }
-    virtual bool isOK() const {
-        // Return true if the model was constructed successfully (not
-        // necessarily whether an error has occurred since
-        // construction, use getError for that)
-        return m_server && m_server->getModel();
-    }
-    virtual sv_frame_t getStartFrame() const {
-        return 0;
-    }
+    virtual int getWidth() const;
+    virtual int getHeight() const;
+    virtual float getValueAt(int x, int y) const { return getMagnitudeAt(x, y); }
+    virtual bool isOK() const { return m_model && m_model->isOK(); }
+    virtual sv_frame_t getStartFrame() const { return 0; }
     virtual sv_frame_t getEndFrame() const {
         return sv_frame_t(getWidth()) * getResolution() + getResolution();
     }
-    virtual sv_samplerate_t getSampleRate() const;
-    virtual int getResolution() const {
-        return m_server->getWindowIncrement() << m_xshift;
+    virtual sv_samplerate_t getSampleRate() const {
+        return isOK() ? m_model->getSampleRate() : 0;
     }
-    virtual int getYBinCount() const {
-        return getHeight();
+    virtual int getResolution() const { return m_windowIncrement; }
+    virtual int 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 Column getColumn(int x) const; // magnitudes
+    virtual QString getBinName(int n) const;
+    virtual bool shouldUseLogValueScale() const { return true; }
+    virtual int getCompletion() const {
+        int c = 100;
+        if (m_model) (void)m_model->isReady(&c);
+        return c;
     }
-    virtual float getMinimumLevel() const {
-        return 0.f; // Can't provide
-    }
-    virtual float getMaximumLevel() const {
-        return 1.f; // Can't provide
-    }
-    virtual Column getColumn(int x) const;
-    virtual QString getBinName(int n) const;
+    virtual QString getError() const { return ""; } //!!!???
+    virtual sv_frame_t getFillExtent() const { return getEndFrame(); }
 
-    virtual bool shouldUseLogValueScale() const {
-        return true; // Although obviously it's up to the user...
-    }
+    // FFTModel methods:
+    //
+    int getChannel() const { return m_channel; }
+    WindowType getWindowType() const { return m_windowType; }
+    int getWindowSize() const { return m_windowSize; }
+    int getWindowIncrement() const { return m_windowIncrement; }
+    int getFFTSize() const { return m_fftSize; }
+    
+    float getMagnitudeAt(int x, int y) const;
+    float getNormalizedMagnitudeAt(int x, int y) const;
+    float getMaximumMagnitudeAt(int x) const;
+    float getPhaseAt(int x, int y) const;
+    void getValuesAt(int x, int y, float &real, float &imaginary) const;
+    bool isColumnAvailable(int x) const;
+    bool getMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) const;
+    bool getNormalizedMagnitudesAt(int x, float *values, int minbin = 0, int count = 0) const;
+    bool getPhasesAt(int x, float *values, int minbin = 0, int count = 0) const;
+    bool getValuesAt(int x, float *reals, float *imaginaries, int minbin = 0, int count = 0) const;
 
     /**
      * Calculate an estimated frequency for a stable signal in this
@@ -179,13 +128,6 @@
     virtual PeakSet getPeakFrequencies(PeakPickType type, int x,
                                        int ymin = 0, int ymax = 0);
 
-    virtual int getCompletion() const { return m_server->getFillCompletion(); }
-    virtual QString getError() const { return m_server->getError(); }
-
-    virtual void suspend() { m_server->suspend(); }
-    virtual void suspendWrites() { m_server->suspendWrites(); }
-    virtual void resume() { m_server->resume(); }
-
     QString getTypeName() const { return tr("FFT"); }
 
 public slots:
@@ -195,23 +137,16 @@
     FFTModel(const FFTModel &); // not implemented
     FFTModel &operator=(const FFTModel &); // not implemented
 
-    FFTDataServer *m_server;
-    int m_xshift;
-    int m_yshift;
-
-    FFTDataServer *getServer(const DenseTimeValueModel *,
-                             int, WindowType, int, int, int,
-                             bool, StorageAdviser::Criteria, sv_frame_t);
-
+    const DenseTimeValueModel *m_model;
+    int m_channel;
+    WindowType m_windowType;
+    int m_windowSize;
+    int m_windowIncrement;
+    int m_fftSize;
+    Window<float> m_windower;
+    
     int getPeakPickWindowSize(PeakPickType type, sv_samplerate_t sampleRate,
                               int bin, float &percentile) const;
-
-    int getYRatio() {
-        int ys = m_yshift;
-        int r = 1;
-        while (ys) { --ys; r <<= 1; }
-        return r;
-    }
 };
 
 #endif
--- a/svcore.pro	Fri Jun 12 13:46:44 2015 +0100
+++ b/svcore.pro	Fri Jun 12 14:51:46 2015 +0100
@@ -119,13 +119,6 @@
            base/XmlExportable.cpp
 
 HEADERS += data/fft/FFTapi.h \
-           data/fft/FFTCacheReader.h \
-           data/fft/FFTCacheStorageType.h \
-           data/fft/FFTCacheWriter.h \
-           data/fft/FFTDataServer.h \
-           data/fft/FFTFileCacheReader.h \
-           data/fft/FFTFileCacheWriter.h \
-           data/fft/FFTMemoryCache.h \
            data/fileio/AudioFileReader.h \
            data/fileio/AudioFileReaderFactory.h \
            data/fileio/BZipFileDevice.h \
@@ -184,10 +177,6 @@
            data/osc/OSCMessage.h \
            data/osc/OSCQueue.h 
 SOURCES += data/fft/FFTapi.cpp \
-           data/fft/FFTDataServer.cpp \
-           data/fft/FFTFileCacheReader.cpp \
-           data/fft/FFTFileCacheWriter.cpp \
-           data/fft/FFTMemoryCache.cpp \
            data/fileio/AudioFileReader.cpp \
            data/fileio/AudioFileReaderFactory.cpp \
            data/fileio/BZipFileDevice.cpp \
--- a/transform/FeatureExtractionModelTransformer.cpp	Fri Jun 12 13:46:44 2015 +0100
+++ b/transform/FeatureExtractionModelTransformer.cpp	Fri Jun 12 14:51:46 2015 +0100
@@ -606,9 +606,7 @@
                                    primaryTransform.getWindowType(),
                                    blockSize,
                                    stepSize,
-                                   blockSize,
-                                   false,
-                                   StorageAdviser::PrecisionCritical);
+                                   blockSize);
             if (!model->isOK() || model->getError() != "") {
                 QString err = model->getError();
                 delete model;
@@ -618,7 +616,6 @@
                 //!!! need a better way to handle this -- previously we were using a QMessageBox but that isn't an appropriate thing to do here either
                 throw AllocationFailed("Failed to create the FFT model for this feature extraction model transformer: error is: " + err);
             }
-            model->resume();
             fftModels.push_back(model);
             cerr << "created model for channel " << ch << endl;
         }