changeset 96:1aebdc68ec6d

* Introduce simple non-RT thread base class * Rename MatrixFileCache to MatrixFile * some fixes & tidying
author Chris Cannam
date Thu, 04 May 2006 16:03:02 +0000 (2006-05-04)
parents 040a151d0897
children 22494cc28c9f
files base/FFTFileCache.cpp base/FFTFileCache.h base/FileReadThread.cpp base/FileReadThread.h base/MatrixFile.cpp base/MatrixFile.h base/MatrixFileCache.cpp base/MatrixFileCache.h base/NonRTThread.cpp base/NonRTThread.h plugin/DSSIPluginInstance.h transform/Transform.h
diffstat 12 files changed, 689 insertions(+), 573 deletions(-) [+]
line wrap: on
line diff
--- a/base/FFTFileCache.cpp	Thu May 04 13:59:57 2006 +0000
+++ b/base/FFTFileCache.cpp	Thu May 04 16:03:02 2006 +0000
@@ -15,7 +15,7 @@
 
 #include "FFTFileCache.h"
 
-#include "MatrixFileCache.h"
+#include "MatrixFile.h"
 
 #include <iostream>
 
@@ -28,9 +28,9 @@
 // etc, and then store the normalization factor (maximum magnitude) at
 // [m_height * 2].
 
-FFTFileCache::FFTFileCache(QString fileBase, MatrixFileCache::Mode mode) :
+FFTFileCache::FFTFileCache(QString fileBase, MatrixFile::Mode mode) :
     m_colbuf(0),
-    m_mfc(new MatrixFileCache(fileBase, mode))
+    m_mfc(new MatrixFile(fileBase, mode))
 {
 }
 
--- a/base/FFTFileCache.h	Thu May 04 13:59:57 2006 +0000
+++ b/base/FFTFileCache.h	Thu May 04 16:03:02 2006 +0000
@@ -17,7 +17,7 @@
 #define _FFT_FILE_CACHE_H_
 
 #include "FFTCache.h"
-#include "MatrixFileCache.h"
+#include "MatrixFile.h"
 
 class FFTFileCache : public FFTCacheBase
 {
@@ -37,7 +37,7 @@
     // plugins get data from it -- need the reader thread to be able
     // to block waiting for the writer thread as appropriate.
 
-    FFTFileCache(QString fileBase, MatrixFileCache::Mode mode);
+    FFTFileCache(QString fileBase, MatrixFile::Mode mode);
     virtual ~FFTFileCache();
 
     virtual size_t getWidth() const;
@@ -60,7 +60,7 @@
 
 protected:
     float *m_colbuf;
-    MatrixFileCache *m_mfc;
+    MatrixFile *m_mfc;
 };
 
 #endif
--- a/base/FileReadThread.cpp	Thu May 04 13:59:57 2006 +0000
+++ b/base/FileReadThread.cpp	Thu May 04 16:03:02 2006 +0000
@@ -33,8 +33,8 @@
             m_condition.wait(&m_mutex, 1000);
         } else {
             process();
-            notifyCancelled();
         }
+        notifyCancelled();
     }
 
     notifyCancelled();
@@ -86,9 +86,18 @@
         m_cancelledRequests[token] = m_queue[token];
         m_queue.erase(token);
         m_newlyCancelled.insert(token);
+    } else if (m_readyRequests.find(token) != m_readyRequests.end()) {
+        m_cancelledRequests[token] = m_readyRequests[token];
+        m_readyRequests.erase(token);
+    } else {
+        std::cerr << "WARNING: FileReadThread::cancel: token " << token << " not found" << std::endl;
     }
 
     m_mutex.unlock();
+
+    std::cerr << "FileReadThread::cancel(" << token << ") waking condition" << std::endl;
+
+    m_condition.wakeAll();
 }
 
 bool
@@ -184,7 +193,29 @@
     if (::lseek(request.fd, request.start, SEEK_SET) == (off_t)-1) {
         seekFailed = true;
     } else {
-        r = ::read(request.fd, request.data, request.size);
+        
+        // if request.size is large, we want to avoid making a single
+        // system call to read it all as it may block too much
+
+        static const size_t blockSize = 16384;
+        
+        size_t size = request.size;
+        char *destination = request.data;
+
+        while (size > 0) {
+            size_t readSize = size;
+            if (readSize > blockSize) readSize = blockSize;
+            ssize_t br = ::read(request.fd, destination, readSize);
+            if (br < 0) { 
+                r = br;
+                break;
+            } else {
+                r += br;
+                if (br < ssize_t(readSize)) break;
+            }
+            destination += readSize;
+            size -= readSize;
+        }
     }
 
     if (request.mutex) request.mutex->unlock();
@@ -229,7 +260,11 @@
     // entered with m_mutex locked
 
     while (!m_newlyCancelled.empty()) {
+
         int token = *m_newlyCancelled.begin();
+
+        std::cerr << "FileReadThread::notifyCancelled: token " << token << std::endl;
+
         m_newlyCancelled.erase(token);
         emit cancelled(token);
     }
--- a/base/FileReadThread.h	Thu May 04 13:59:57 2006 +0000
+++ b/base/FileReadThread.h	Thu May 04 16:03:02 2006 +0000
@@ -16,7 +16,8 @@
 #ifndef _FILE_READ_THREAD_H_
 #define _FILE_READ_THREAD_H_
 
-#include <QThread>
+#include "NonRTThread.h"
+
 #include <QMutex>
 #include <QWaitCondition>
 
@@ -25,7 +26,7 @@
 
 #include <stdint.h>
 
-class FileReadThread : public QThread
+class FileReadThread : public NonRTThread
 {
     Q_OBJECT
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/MatrixFile.cpp	Thu May 04 16:03:02 2006 +0000
@@ -0,0 +1,479 @@
+/* -*- 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 "MatrixFile.h"
+#include "base/TempDirectory.h"
+#include "base/System.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <iostream>
+
+#include <cstdio>
+
+#include <QFileInfo>
+#include <QDir>
+
+std::map<QString, int> MatrixFile::m_refcount;
+QMutex MatrixFile::m_refcountMutex;
+
+MatrixFile::MatrixFile(QString fileBase, Mode mode) :
+    m_fd(-1),
+    m_mode(mode),
+    m_width(0),
+    m_height(0),
+    m_headerSize(2 * sizeof(size_t)),
+    m_defaultCacheWidth(256),
+    m_prevX(0),
+    m_requestToken(-1)
+{
+    m_cache.data = 0;
+
+    QDir tempDir(TempDirectory::instance()->getPath());
+    QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase)));
+    bool newFile = !QFileInfo(fileName).exists();
+
+    if (newFile && mode == ReadOnly) {
+        std::cerr << "ERROR: MatrixFile::MatrixFile: Read-only mode "
+                  << "specified, but cache file does not exist" << std::endl;
+        return;
+    }
+
+    int flags = 0;
+    mode_t fmode = S_IRUSR | S_IWUSR;
+
+    if (mode == ReadWrite) {
+        flags = O_RDWR | O_CREAT;
+    } else {
+        flags = O_RDONLY;
+    }
+
+    if ((m_fd = ::open(fileName.toLocal8Bit(), flags, fmode)) < 0) {
+        ::perror("Open failed");
+        std::cerr << "ERROR: MatrixFile::MatrixFile: "
+                  << "Failed to open cache file \""
+                  << fileName.toStdString() << "\"";
+        if (mode == ReadWrite) std::cerr << " for writing";
+        std::cerr << std::endl;
+        return;
+    }
+
+    if (newFile) {
+        resize(0, 0); // write header
+    } else {
+        size_t header[2];
+        if (::read(m_fd, header, 2 * sizeof(size_t)) < 0) {
+            perror("Read failed");
+            std::cerr << "ERROR: MatrixFile::MatrixFile: "
+                      << "Failed to read header (fd " << m_fd << ", file \""
+                      << fileName.toStdString() << "\")" << std::endl;
+            return;
+        }
+        m_width = header[0];
+        m_height = header[1];
+        seekTo(0, 0);
+    }
+
+    m_fileName = fileName;
+    
+    //!!! why isn't this signal being delivered?
+    connect(&m_readThread, SIGNAL(cancelled(int)), 
+            this, SLOT(requestCancelled(int)));
+
+    m_readThread.start();
+
+    QMutexLocker locker(&m_refcountMutex);
+    ++m_refcount[fileName];
+
+    std::cerr << "MatrixFile::MatrixFile: Done, size is " << "(" << m_width << ", " << m_height << ")" << std::endl;
+
+}
+
+MatrixFile::~MatrixFile()
+{
+    float *requestData = 0;
+
+    if (m_requestToken >= 0) {
+        FileReadThread::Request request;
+        if (m_readThread.getRequest(m_requestToken, request)) {
+            requestData = (float *)request.data;
+        }
+    }
+
+    m_readThread.finish();
+    m_readThread.wait();
+
+    if (requestData) delete[] requestData;
+    if (m_cache.data) delete[] m_cache.data;
+
+    if (m_fd >= 0) {
+        if (::close(m_fd) < 0) {
+            ::perror("MatrixFile::~MatrixFile: close failed");
+        }
+    }
+
+    if (m_fileName != "") {
+        QMutexLocker locker(&m_refcountMutex);
+        if (--m_refcount[m_fileName] == 0) {
+            if (::unlink(m_fileName.toLocal8Bit())) {
+                ::perror("Unlink failed");
+                std::cerr << "WARNING: MatrixFile::~MatrixFile: reference count reached 0, but failed to unlink file \"" << m_fileName.toStdString() << "\"" << std::endl;
+            } else {
+                std::cerr << "deleted " << m_fileName.toStdString() << std::endl;
+            }
+        }
+    }
+}
+
+size_t 
+MatrixFile::getWidth() const
+{
+    return m_width;
+}
+
+size_t
+MatrixFile::getHeight() const
+{
+    return m_height;
+}
+
+void
+MatrixFile::resize(size_t w, size_t h)
+{
+    if (m_mode != ReadWrite) {
+        std::cerr << "ERROR: MatrixFile::resize called on read-only cache"
+                  << std::endl;
+        return;
+    }
+
+    QMutexLocker locker(&m_fdMutex);
+    
+    off_t off = m_headerSize + (w * h * sizeof(float));
+
+    if (w * h > m_width * m_height) {
+
+        if (::lseek(m_fd, off - sizeof(float), SEEK_SET) == (off_t)-1) {
+            ::perror("Seek failed");
+            std::cerr << "ERROR: MatrixFile::resize(" << w << ", "
+                      << h << "): seek failed, cannot resize" << std::endl;
+            return;
+        }
+            
+        // guess this requires efficient support for sparse files
+        
+        float f(0);
+        if (::write(m_fd, &f, sizeof(float)) != sizeof(float)) {
+            ::perror("WARNING: MatrixFile::resize: write failed");
+        }
+
+    } else {
+        
+        if (::ftruncate(m_fd, off) < 0) {
+            ::perror("WARNING: MatrixFile::resize: ftruncate failed");
+        }
+    }
+
+    m_width = 0;
+    m_height = 0;
+
+    if (::lseek(m_fd, 0, SEEK_SET) == (off_t)-1) {
+        ::perror("ERROR: MatrixFile::resize: Seek to write header failed");
+        return;
+    }
+
+    size_t header[2];
+    header[0] = w;
+    header[1] = h;
+    if (::write(m_fd, header, 2 * sizeof(size_t)) != 2 * sizeof(size_t)) {
+        ::perror("ERROR: MatrixFile::resize: Failed to write header");
+        return;
+    }
+
+    m_width = w;
+    m_height = h;
+
+    seekTo(0, 0);
+}
+
+void
+MatrixFile::reset()
+{
+    if (m_mode != ReadWrite) {
+        std::cerr << "ERROR: MatrixFile::reset called on read-only cache"
+                  << std::endl;
+        return;
+    }
+    
+    QMutexLocker locker(&m_fdMutex);
+
+    float *emptyCol = new float[m_height];
+    for (size_t y = 0; y < m_height; ++y) emptyCol[y] = 0.f;
+
+    seekTo(0, 0);
+    for (size_t x = 0; x < m_width; ++x) setColumnAt(x, emptyCol);
+    
+    delete[] emptyCol;
+}
+
+float
+MatrixFile::getValueAt(size_t x, size_t y)
+{
+    float value = 0.f;
+    if (getValuesFromCache(x, y, 1, &value)) return value;
+
+    ssize_t r = 0;
+
+//    std::cout << "MatrixFile::getValueAt(" << x << ", " << y << ")"
+//              << ": reading the slow way" << std::endl;
+
+    m_fdMutex.lock();
+
+    if (seekTo(x, y)) {
+        r = ::read(m_fd, &value, sizeof(float));
+    }
+
+    m_fdMutex.unlock();
+
+    if (r < 0) {
+        ::perror("MatrixFile::getValueAt: Read failed");
+    }
+    if (r != sizeof(float)) {
+        value = 0.f;
+    }
+
+    return value;
+}
+
+void
+MatrixFile::getColumnAt(size_t x, float *values)
+{
+    if (getValuesFromCache(x, 0, m_height, values)) return;
+
+    ssize_t r = 0;
+
+    std::cout << "MatrixFile::getColumnAt(" << x << ")"
+              << ": reading the slow way" << std::endl;
+
+    m_fdMutex.lock();
+
+    if (seekTo(x, 0)) {
+        r = ::read(m_fd, values, m_height * sizeof(float));
+    }
+
+    m_fdMutex.unlock();
+    
+    if (r < 0) {
+        ::perror("MatrixFile::getColumnAt: read failed");
+    }
+}
+
+bool
+MatrixFile::getValuesFromCache(size_t x, size_t ystart, size_t ycount,
+                                    float *values)
+{
+    m_cacheMutex.lock();
+
+    if (!m_cache.data || x < m_cache.x || x >= m_cache.x + m_cache.width) {
+        bool left = (m_cache.data && x < m_cache.x);
+        m_cacheMutex.unlock();
+        primeCache(x, left); // this doesn't take effect until a later callback
+        m_prevX = x;
+        return false;
+    }
+
+    for (size_t y = ystart; y < ystart + ycount; ++y) {
+        values[y - ystart] = m_cache.data[(x - m_cache.x) * m_height + y];
+    }
+    m_cacheMutex.unlock();
+
+    if (m_cache.x > 0 && x < m_prevX && x < m_cache.x + m_cache.width/4) {
+        primeCache(x, true);
+    }
+
+    if (m_cache.x + m_cache.width < m_width &&
+        x > m_prevX &&
+        x > m_cache.x + (m_cache.width * 3) / 4) {
+        primeCache(x, false);
+    }
+
+    m_prevX = x;
+    return true;
+}
+
+void
+MatrixFile::setValueAt(size_t x, size_t y, float value)
+{
+    if (m_mode != ReadWrite) {
+        std::cerr << "ERROR: MatrixFile::setValueAt called on read-only cache"
+                  << std::endl;
+        return;
+    }
+
+    ssize_t w = 0;
+    bool seekFailed = false;
+
+    m_fdMutex.lock();
+
+    if (seekTo(x, y)) {
+        w = ::write(m_fd, &value, sizeof(float));
+    } else {
+        seekFailed = true;
+    }
+
+    m_fdMutex.unlock();
+
+    if (!seekFailed && w != sizeof(float)) {
+        ::perror("WARNING: MatrixFile::setValueAt: write failed");
+    }
+
+    //... update cache as appropriate
+}
+
+void
+MatrixFile::setColumnAt(size_t x, float *values)
+{
+    if (m_mode != ReadWrite) {
+        std::cerr << "ERROR: MatrixFile::setColumnAt called on read-only cache"
+                  << std::endl;
+        return;
+    }
+
+    ssize_t w = 0;
+    bool seekFailed = false;
+
+    m_fdMutex.lock();
+
+    if (seekTo(x, 0)) {
+        w = ::write(m_fd, values, m_height * sizeof(float));
+    } else {
+        seekFailed = true;
+    }
+
+    m_fdMutex.unlock();
+
+    if (!seekFailed && w != ssize_t(m_height * sizeof(float))) {
+        ::perror("WARNING: MatrixFile::setColumnAt: write failed");
+    }
+
+    //... update cache as appropriate
+}
+
+void
+MatrixFile::primeCache(size_t x, bool goingLeft)
+{
+//    std::cerr << "MatrixFile::primeCache(" << x << ", " << goingLeft << ")" << std::endl;
+
+    size_t rx = x;
+    size_t rw = m_defaultCacheWidth;
+
+    size_t left = rw / 3;
+    if (goingLeft) left = (rw * 2) / 3;
+
+    if (rx > left) rx -= left;
+    else rx = 0;
+
+    if (rx + rw > m_width) rw = m_width - rx;
+
+    QMutexLocker locker(&m_cacheMutex);
+
+    FileReadThread::Request request;
+
+    if (m_requestToken >= 0 &&
+        m_readThread.getRequest(m_requestToken, request)) {
+
+        if (x >= m_requestingX &&
+            x <  m_requestingX + m_requestingWidth) {
+
+            if (m_readThread.isReady(m_requestToken)) {
+
+                std::cerr << "last request is ready! (" << m_requestingX << ", "<< m_requestingWidth << ")"  << std::endl;
+
+                m_cache.x = (request.start - m_headerSize) / (m_height * sizeof(float));
+                m_cache.width = request.size / (m_height * sizeof(float));
+                
+                std::cerr << "actual: " << m_cache.x << ", " << m_cache.width << std::endl;
+
+                if (m_cache.data) delete[] m_cache.data;
+                m_cache.data = (float *)request.data;
+
+                m_readThread.done(m_requestToken);
+                m_requestToken = -1;
+            }
+
+            // already requested something covering this area; wait for it
+            return;
+        }
+
+        // the current request is no longer of any use
+        m_readThread.cancel(m_requestToken);
+
+        // crude way to avoid leaking the data
+        while (!m_readThread.isCancelled(m_requestToken)) {
+            usleep(10000);
+        }
+
+        delete[] ((float *)request.data);
+        m_readThread.done(m_requestToken);
+
+        m_requestToken = -1;
+    }
+
+    request.fd = m_fd;
+    request.mutex = &m_fdMutex;
+    request.start = m_headerSize + rx * m_height * sizeof(float);
+    request.size = rw * m_height * sizeof(float);
+    request.data = (char *)(new float[rw * m_height]);
+    MUNLOCK(request.data, rw * m_height * sizeof(float));
+
+    m_requestingX = rx;
+    m_requestingWidth = rw;
+
+    int token = m_readThread.request(request);
+    std::cerr << "MatrixFile::primeCache: request token is "
+              << token << " (x = " << rx << ", w = " << rw << ", left = " << goingLeft << ")" << std::endl;
+
+    m_requestToken = token;
+}
+
+void
+MatrixFile::requestCancelled(int token)
+{
+    std::cerr << "MatrixFile::requestCancelled(" << token << ")" << std::endl;
+
+    FileReadThread::Request request;
+    if (m_readThread.getRequest(token, request)) {
+        delete[] ((float *)request.data);
+        m_readThread.done(token);
+    }
+}
+
+bool
+MatrixFile::seekTo(size_t x, size_t y)
+{
+    off_t off = m_headerSize + (x * m_height + y) * sizeof(float);
+
+    if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) {
+        ::perror("Seek failed");
+        std::cerr << "ERROR: MatrixFile::seekTo(" << x << ", " << y
+                  << ") failed" << std::endl;
+        return false;
+    }
+
+    return true;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/MatrixFile.h	Thu May 04 16:03:02 2006 +0000
@@ -0,0 +1,88 @@
+/* -*- 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 _MATRIX_FILE_CACHE_H_
+#define _MATRIX_FILE_CACHE_H_
+
+#include <sys/types.h>
+#include <QString>
+#include <QMutex>
+#include <map>
+
+#include "FileReadThread.h"
+
+class MatrixFile : public QObject
+{
+    Q_OBJECT
+
+public:
+    enum Mode { ReadOnly, ReadWrite };
+
+    MatrixFile(QString fileBase, Mode mode);
+    virtual ~MatrixFile();
+
+    size_t getWidth() const;
+    size_t getHeight() const;
+    
+    void resize(size_t width, size_t height);
+    void reset();
+
+    float getValueAt(size_t x, size_t y);
+    void getColumnAt(size_t x, float *values);
+
+    void setValueAt(size_t x, size_t y, float value);
+    void setColumnAt(size_t x, float *values);
+
+protected slots:
+    void requestCancelled(int token);
+
+protected:
+    int     m_fd;
+    Mode    m_mode;
+    size_t  m_width;
+    size_t  m_height;
+    size_t  m_headerSize;
+    QString m_fileName;
+    size_t  m_defaultCacheWidth;
+    size_t  m_prevX;
+
+    struct Cache {
+        size_t  x;
+        size_t  width;
+        float  *data;
+    };
+
+    Cache m_cache;
+
+    bool getValuesFromCache(size_t x, size_t ystart, size_t ycount,
+                            float *values);
+
+    void primeCache(size_t x, bool left);
+
+    bool seekTo(size_t x, size_t y);
+
+    FileReadThread m_readThread;
+    int m_requestToken;
+    size_t m_requestingX;
+    size_t m_requestingWidth;
+
+    static std::map<QString, int> m_refcount;
+    static QMutex m_refcountMutex;
+    QMutex m_fdMutex;
+    QMutex m_cacheMutex;
+};
+
+#endif
+
--- a/base/MatrixFileCache.cpp	Thu May 04 13:59:57 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,471 +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 "MatrixFileCache.h"
-#include "base/TempDirectory.h"
-#include "base/System.h"
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include <iostream>
-
-#include <cstdio>
-
-#include <QFileInfo>
-#include <QDir>
-
-std::map<QString, int> MatrixFileCache::m_refcount;
-QMutex MatrixFileCache::m_refcountMutex;
-
-MatrixFileCache::MatrixFileCache(QString fileBase, Mode mode) :
-    m_fd(-1),
-    m_mode(mode),
-    m_width(0),
-    m_height(0),
-    m_headerSize(2 * sizeof(size_t)),
-    m_defaultCacheWidth(2048),
-    m_prevX(0),
-    m_requestToken(-1)
-{
-    m_cache.data = 0;
-
-    QDir tempDir(TempDirectory::instance()->getPath());
-    QString fileName(tempDir.filePath(QString("%1.mfc").arg(fileBase)));
-    bool newFile = !QFileInfo(fileName).exists();
-
-    if (newFile && mode == ReadOnly) {
-        std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: Read-only mode "
-                  << "specified, but cache file does not exist" << std::endl;
-        return;
-    }
-
-    int flags = 0;
-    mode_t fmode = S_IRUSR | S_IWUSR;
-
-    if (mode == ReadWrite) {
-        flags = O_RDWR | O_CREAT;
-    } else {
-        flags = O_RDONLY;
-    }
-
-    if ((m_fd = ::open(fileName.toLocal8Bit(), flags, fmode)) < 0) {
-        ::perror("Open failed");
-        std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: "
-                  << "Failed to open cache file \""
-                  << fileName.toStdString() << "\"";
-        if (mode == ReadWrite) std::cerr << " for writing";
-        std::cerr << std::endl;
-        return;
-    }
-
-    if (newFile) {
-        resize(0, 0); // write header
-    } else {
-        size_t header[2];
-        if (::read(m_fd, header, 2 * sizeof(size_t)) < 0) {
-            perror("Read failed");
-            std::cerr << "ERROR: MatrixFileCache::MatrixFileCache: "
-                      << "Failed to read header (fd " << m_fd << ", file \""
-                      << fileName.toStdString() << "\")" << std::endl;
-            return;
-        }
-        m_width = header[0];
-        m_height = header[1];
-        seekTo(0, 0);
-    }
-
-    m_fileName = fileName;
-    
-    //!!! why isn't this signal being delivered?
-    connect(&m_readThread, SIGNAL(cancelled(int)), 
-            this, SLOT(requestCancelled(int)));
-
-    m_readThread.start();
-
-    QMutexLocker locker(&m_refcountMutex);
-    ++m_refcount[fileName];
-
-    std::cerr << "MatrixFileCache::MatrixFileCache: Done, size is " << "(" << m_width << ", " << m_height << ")" << std::endl;
-
-}
-
-MatrixFileCache::~MatrixFileCache()
-{
-    float *requestData = 0;
-
-    if (m_requestToken >= 0) {
-        FileReadThread::Request request;
-        if (m_readThread.getRequest(m_requestToken, request)) {
-            requestData = (float *)request.data;
-        }
-    }
-
-    m_readThread.finish();
-    m_readThread.wait();
-
-    if (requestData) delete[] requestData;
-    if (m_cache.data) delete[] m_cache.data;
-
-    if (m_fd >= 0) {
-        if (::close(m_fd) < 0) {
-            ::perror("MatrixFileCache::~MatrixFileCache: close failed");
-        }
-    }
-
-    if (m_fileName != "") {
-        QMutexLocker locker(&m_refcountMutex);
-        if (--m_refcount[m_fileName] == 0) {
-            if (::unlink(m_fileName.toLocal8Bit())) {
-                ::perror("Unlink failed");
-                std::cerr << "WARNING: MatrixFileCache::~MatrixFileCache: reference count reached 0, but failed to unlink file \"" << m_fileName.toStdString() << "\"" << std::endl;
-            } else {
-                std::cerr << "deleted " << m_fileName.toStdString() << std::endl;
-            }
-        }
-    }
-}
-
-size_t 
-MatrixFileCache::getWidth() const
-{
-    return m_width;
-}
-
-size_t
-MatrixFileCache::getHeight() const
-{
-    return m_height;
-}
-
-void
-MatrixFileCache::resize(size_t w, size_t h)
-{
-    if (m_mode != ReadWrite) {
-        std::cerr << "ERROR: MatrixFileCache::resize called on read-only cache"
-                  << std::endl;
-        return;
-    }
-
-    QMutexLocker locker(&m_fdMutex);
-    
-    off_t off = m_headerSize + (w * h * sizeof(float));
-
-    if (w * h > m_width * m_height) {
-
-        if (::lseek(m_fd, off - sizeof(float), SEEK_SET) == (off_t)-1) {
-            ::perror("Seek failed");
-            std::cerr << "ERROR: MatrixFileCache::resize(" << w << ", "
-                      << h << "): seek failed, cannot resize" << std::endl;
-            return;
-        }
-            
-        // guess this requires efficient support for sparse files
-        
-        float f(0);
-        if (::write(m_fd, &f, sizeof(float)) != sizeof(float)) {
-            ::perror("WARNING: MatrixFileCache::resize: write failed");
-        }
-
-    } else {
-        
-        if (::ftruncate(m_fd, off) < 0) {
-            ::perror("WARNING: MatrixFileCache::resize: ftruncate failed");
-        }
-    }
-
-    m_width = 0;
-    m_height = 0;
-
-    if (::lseek(m_fd, 0, SEEK_SET) == (off_t)-1) {
-        ::perror("ERROR: MatrixFileCache::resize: Seek to write header failed");
-        return;
-    }
-
-    size_t header[2];
-    header[0] = w;
-    header[1] = h;
-    if (::write(m_fd, header, 2 * sizeof(size_t)) != 2 * sizeof(size_t)) {
-        ::perror("ERROR: MatrixFileCache::resize: Failed to write header");
-        return;
-    }
-
-    m_width = w;
-    m_height = h;
-
-    seekTo(0, 0);
-}
-
-void
-MatrixFileCache::reset()
-{
-    if (m_mode != ReadWrite) {
-        std::cerr << "ERROR: MatrixFileCache::reset called on read-only cache"
-                  << std::endl;
-        return;
-    }
-    
-    QMutexLocker locker(&m_fdMutex);
-
-    float *emptyCol = new float[m_height];
-    for (size_t y = 0; y < m_height; ++y) emptyCol[y] = 0.f;
-
-    seekTo(0, 0);
-    for (size_t x = 0; x < m_width; ++x) setColumnAt(x, emptyCol);
-    
-    delete[] emptyCol;
-}
-
-float
-MatrixFileCache::getValueAt(size_t x, size_t y)
-{
-    float value = 0.f;
-    if (getValuesFromCache(x, y, 1, &value)) return value;
-
-    ssize_t r = 0;
-
-//    std::cout << "MatrixFileCache::getValueAt(" << x << ", " << y << ")"
-//              << ": reading the slow way" << std::endl;
-
-    m_fdMutex.lock();
-
-    if (seekTo(x, y)) {
-        r = ::read(m_fd, &value, sizeof(float));
-    }
-
-    m_fdMutex.unlock();
-
-    if (r < 0) {
-        ::perror("MatrixFileCache::getValueAt: Read failed");
-    }
-    if (r != sizeof(float)) {
-        value = 0.f;
-    }
-
-    return value;
-}
-
-void
-MatrixFileCache::getColumnAt(size_t x, float *values)
-{
-    if (getValuesFromCache(x, 0, m_height, values)) return;
-
-    ssize_t r = 0;
-
-    std::cout << "MatrixFileCache::getColumnAt(" << x << ")"
-              << ": reading the slow way" << std::endl;
-
-    m_fdMutex.lock();
-
-    if (seekTo(x, 0)) {
-        r = ::read(m_fd, values, m_height * sizeof(float));
-    }
-
-    m_fdMutex.unlock();
-    
-    if (r < 0) {
-        ::perror("MatrixFileCache::getColumnAt: read failed");
-    }
-}
-
-bool
-MatrixFileCache::getValuesFromCache(size_t x, size_t ystart, size_t ycount,
-                                    float *values)
-{
-    m_cacheMutex.lock();
-
-    if (!m_cache.data || x < m_cache.x || x >= m_cache.x + m_cache.width) {
-        bool left = (m_cache.data && x < m_cache.x);
-        m_cacheMutex.unlock();
-        primeCache(x, left); // this doesn't take effect until a later callback
-        m_prevX = x;
-        return false;
-    }
-
-    for (size_t y = ystart; y < ystart + ycount; ++y) {
-        values[y - ystart] = m_cache.data[(x - m_cache.x) * m_height + y];
-    }
-    m_cacheMutex.unlock();
-
-    if (m_cache.x > 0 && x < m_prevX && x < m_cache.x + m_cache.width/4) {
-        primeCache(x, true);
-    }
-
-    if (m_cache.x + m_cache.width < m_width &&
-        x > m_prevX &&
-        x > m_cache.x + (m_cache.width * 3) / 4) {
-        primeCache(x, false);
-    }
-
-    m_prevX = x;
-    return true;
-}
-
-void
-MatrixFileCache::setValueAt(size_t x, size_t y, float value)
-{
-    if (m_mode != ReadWrite) {
-        std::cerr << "ERROR: MatrixFileCache::setValueAt called on read-only cache"
-                  << std::endl;
-        return;
-    }
-
-    ssize_t w = 0;
-    bool seekFailed = false;
-
-    m_fdMutex.lock();
-
-    if (seekTo(x, y)) {
-        w = ::write(m_fd, &value, sizeof(float));
-    } else {
-        seekFailed = true;
-    }
-
-    m_fdMutex.unlock();
-
-    if (!seekFailed && w != sizeof(float)) {
-        ::perror("WARNING: MatrixFileCache::setValueAt: write failed");
-    }
-
-    //... update cache as appropriate
-}
-
-void
-MatrixFileCache::setColumnAt(size_t x, float *values)
-{
-    if (m_mode != ReadWrite) {
-        std::cerr << "ERROR: MatrixFileCache::setColumnAt called on read-only cache"
-                  << std::endl;
-        return;
-    }
-
-    ssize_t w = 0;
-    bool seekFailed = false;
-
-    m_fdMutex.lock();
-
-    if (seekTo(x, 0)) {
-        w = ::write(m_fd, values, m_height * sizeof(float));
-    } else {
-        seekFailed = true;
-    }
-
-    m_fdMutex.unlock();
-
-    if (!seekFailed && w != ssize_t(m_height * sizeof(float))) {
-        ::perror("WARNING: MatrixFileCache::setColumnAt: write failed");
-    }
-
-    //... update cache as appropriate
-}
-
-void
-MatrixFileCache::primeCache(size_t x, bool goingLeft)
-{
-//    std::cerr << "MatrixFileCache::primeCache(" << x << ", " << goingLeft << ")" << std::endl;
-
-    size_t rx = x;
-    size_t rw = m_defaultCacheWidth;
-
-    size_t left = rw / 3;
-    if (goingLeft) left = (rw * 2) / 3;
-
-    if (rx > left) rx -= left;
-    else rx = 0;
-
-    if (rx + rw > m_width) rw = m_width - rx;
-
-    QMutexLocker locker(&m_cacheMutex);
-
-    if (m_requestToken >= 0) {
-
-        if (x >= m_requestingX &&
-            x <  m_requestingX + m_requestingWidth) {
-
-            if (m_readThread.isReady(m_requestToken)) {
-
-                std::cerr << "last request is ready! (" << m_requestingX << ", "<< m_requestingWidth << ")"  << std::endl;
-
-                FileReadThread::Request request;
-                if (m_readThread.getRequest(m_requestToken, request)) {
-
-                    m_cache.x = (request.start - m_headerSize) / (m_height * sizeof(float));
-                    m_cache.width = request.size / (m_height * sizeof(float));
-                    
-                    std::cerr << "actual: " << m_cache.x << ", " << m_cache.width << std::endl;
-
-                    if (m_cache.data) delete[] m_cache.data;
-                    m_cache.data = (float *)request.data;
-                }
-                
-                m_readThread.done(m_requestToken);
-                m_requestToken = -1;
-            }
-
-            return;
-        }
-
-        std::cerr << "cancelling last request" << std::endl;
-        m_readThread.cancel(m_requestToken);
-//!!!
-        m_requestToken = -1;
-    }
-
-    FileReadThread::Request request;
-    request.fd = m_fd;
-    request.mutex = &m_fdMutex;
-    request.start = m_headerSize + rx * m_height * sizeof(float);
-    request.size = rw * m_height * sizeof(float);
-    request.data = (char *)(new float[rw * m_height]);
-
-    m_requestingX = rx;
-    m_requestingWidth = rw;
-
-    int token = m_readThread.request(request);
-    std::cerr << "MatrixFileCache::primeCache: request token is "
-              << token << std::endl;
-
-    m_requestToken = token;
-}
-
-void
-MatrixFileCache::requestCancelled(int token)
-{
-    std::cerr << "MatrixFileCache::requestCancelled(" << token << ")" << std::endl;
-
-    FileReadThread::Request request;
-    if (m_readThread.getRequest(token, request)) {
-        delete[] ((float *)request.data);
-        m_readThread.done(token);
-    }
-}
-
-bool
-MatrixFileCache::seekTo(size_t x, size_t y)
-{
-    off_t off = m_headerSize + (x * m_height + y) * sizeof(float);
-
-    if (::lseek(m_fd, off, SEEK_SET) == (off_t)-1) {
-        ::perror("Seek failed");
-        std::cerr << "ERROR: MatrixFileCache::seekTo(" << x << ", " << y
-                  << ") failed" << std::endl;
-        return false;
-    }
-
-    return true;
-}
-
--- a/base/MatrixFileCache.h	Thu May 04 13:59:57 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,88 +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 _MATRIX_FILE_CACHE_H_
-#define _MATRIX_FILE_CACHE_H_
-
-#include <sys/types.h>
-#include <QString>
-#include <QMutex>
-#include <map>
-
-#include "FileReadThread.h"
-
-class MatrixFileCache : public QObject
-{
-    Q_OBJECT
-
-public:
-    enum Mode { ReadOnly, ReadWrite };
-
-    MatrixFileCache(QString fileBase, Mode mode);
-    virtual ~MatrixFileCache();
-
-    size_t getWidth() const;
-    size_t getHeight() const;
-    
-    void resize(size_t width, size_t height);
-    void reset();
-
-    float getValueAt(size_t x, size_t y);
-    void getColumnAt(size_t x, float *values);
-
-    void setValueAt(size_t x, size_t y, float value);
-    void setColumnAt(size_t x, float *values);
-
-protected slots:
-    void requestCancelled(int token);
-
-protected:
-    int     m_fd;
-    Mode    m_mode;
-    size_t  m_width;
-    size_t  m_height;
-    size_t  m_headerSize;
-    QString m_fileName;
-    size_t  m_defaultCacheWidth;
-    size_t  m_prevX;
-
-    struct Cache {
-        size_t  x;
-        size_t  width;
-        float  *data;
-    };
-
-    Cache m_cache;
-
-    bool getValuesFromCache(size_t x, size_t ystart, size_t ycount,
-                            float *values);
-
-    void primeCache(size_t x, bool left);
-
-    bool seekTo(size_t x, size_t y);
-
-    FileReadThread m_readThread;
-    int m_requestToken;
-    size_t m_requestingX;
-    size_t m_requestingWidth;
-
-    static std::map<QString, int> m_refcount;
-    static QMutex m_refcountMutex;
-    QMutex m_fdMutex;
-    QMutex m_cacheMutex;
-};
-
-#endif
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/NonRTThread.cpp	Thu May 04 16:03:02 2006 +0000
@@ -0,0 +1,40 @@
+/* -*- 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 "NonRTThread.h"
+
+#include <pthread.h>
+
+NonRTThread::NonRTThread(QObject *parent) :
+    QThread(parent)
+{
+    setStackSize(512 * 1024);
+}
+
+void
+NonRTThread::start()
+{
+    QThread::start();
+
+#ifndef Q_WS_WIN32
+    struct sched_param param;
+    ::memset(&param, 0, sizeof(param));
+
+    if (::pthread_setschedparam(pthread_self(), SCHED_OTHER, &param)) {
+        ::perror("pthread_setschedparam to SCHED_OTHER failed");
+    }
+#endif
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/NonRTThread.h	Thu May 04 16:03:02 2006 +0000
@@ -0,0 +1,32 @@
+/* -*- 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 _NON_RT_THREAD_H_
+#define _NON_RT_THREAD_H_
+
+#include <QThread>
+
+class NonRTThread : public QThread
+{
+Q_OBJECT
+
+public:
+    NonRTThread(QObject *parent = 0);
+
+public slots:
+    void start();
+};
+
+#endif
--- a/plugin/DSSIPluginInstance.h	Thu May 04 13:59:57 2006 +0000
+++ b/plugin/DSSIPluginInstance.h	Thu May 04 16:03:02 2006 +0000
@@ -28,11 +28,11 @@
 #include <map>
 #include <QString>
 #include <QMutex>
-#include <QThread>
 
 #include "api/dssi.h"
 
 #include "base/RingBuffer.h"
+#include "base/NonRTThread.h"
 #include "RealTimePluginInstance.h"
 #include "base/Scavenger.h"
 
@@ -198,7 +198,7 @@
 
     static Scavenger<ScavengerArrayWrapper<snd_seq_event_t *> > m_bufferScavenger;
 
-    class NonRTPluginThread : public QThread
+    class NonRTPluginThread : public NonRTThread
     {
     public:
 	NonRTPluginThread(LADSPA_Handle handle,
--- a/transform/Transform.h	Thu May 04 13:59:57 2006 +0000
+++ b/transform/Transform.h	Thu May 04 16:03:02 2006 +0000
@@ -16,7 +16,7 @@
 #ifndef _TRANSFORM_H_
 #define _TRANSFORM_H_
 
-#include <QThread>
+#include "NonRTThread.h"
 
 #include "base/Model.h"
 
@@ -35,7 +35,7 @@
  * the background thread has populated it.
  */
 
-class Transform : public QThread
+class Transform : public NonRTThread
 {
 public:
     virtual ~Transform();