changeset 102:0a846f83a4b7

* Move matrix/fft file code to fileio (from base) * Add right-button menu to panes
author Chris Cannam
date Fri, 05 May 2006 13:06:47 +0000
parents ce1d385f4f89
children 173b39ea0728
files base/FFTFileCache.cpp base/FFTFileCache.h base/FileReadThread.cpp base/FileReadThread.h base/MatrixFile.cpp base/MatrixFile.h base/View.cpp transform/TransformFactory.cpp
diffstat 8 files changed, 9 insertions(+), 1105 deletions(-) [+]
line wrap: on
line diff
--- a/base/FFTFileCache.cpp	Fri May 05 12:34:51 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,128 +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 "FFTFileCache.h"
-
-#include "MatrixFile.h"
-
-#include <iostream>
-
-//!!! This class is a work in progress -- it does only as much as we
-// need for the current SpectrogramLayer.  Slated for substantial
-// refactoring and extension.
-
-// 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].
-
-FFTFileCache::FFTFileCache(QString fileBase, MatrixFile::Mode mode) :
-    m_colbuf(0),
-    m_mfc(new MatrixFile(fileBase, mode))
-{
-}
-
-FFTFileCache::~FFTFileCache()
-{
-    delete m_colbuf;
-    delete m_mfc;
-}
-
-size_t
-FFTFileCache::getWidth() const
-{
-    return m_mfc->getWidth();
-}
-
-size_t
-FFTFileCache::getHeight() const
-{
-    size_t mh = m_mfc->getHeight();
-    if (mh > 0) return (mh - 1) / 2;
-    else return 0;
-}
-
-void
-FFTFileCache::resize(size_t width, size_t height)
-{
-    m_mfc->resize(width, height * 2 + 1);
-    delete m_colbuf;
-    m_colbuf = new float[height * 2 + 1];
-}
-
-void
-FFTFileCache::reset()
-{
-    m_mfc->reset();
-}
-
-float
-FFTFileCache::getMagnitudeAt(size_t x, size_t y) const
-{
-    return m_mfc->getValueAt(x, y * 2);
-}
-
-float
-FFTFileCache::getNormalizedMagnitudeAt(size_t x, size_t y) const
-{
-    float factor = m_mfc->getValueAt(x, m_mfc->getHeight() - 1);
-    float mag = m_mfc->getValueAt(x, y * 2);
-    if (factor != 0) return mag / factor;
-    else return 0.f;
-}
-
-float
-FFTFileCache::getPhaseAt(size_t x, size_t y) const
-{
-    return m_mfc->getValueAt(x, y * 2 + 1);
-}
-
-void
-FFTFileCache::setNormalizationFactor(size_t x, float factor)
-{
-    m_mfc->setValueAt(x, m_mfc->getHeight() - 1, factor);
-}
-
-void
-FFTFileCache::setMagnitudeAt(size_t x, size_t y, float mag)
-{
-    m_mfc->setValueAt(x, y * 2, mag);
-}
-
-void
-FFTFileCache::setNormalizedMagnitudeAt(size_t x, size_t y, float norm)
-{
-    float factor = m_mfc->getValueAt(x, m_mfc->getHeight() - 1);
-    m_mfc->setValueAt(x, y * 2, norm * factor);
-}
-
-void
-FFTFileCache::setPhaseAt(size_t x, size_t y, float phase)
-{
-    m_mfc->setValueAt(x, y * 2 + 1, phase);
-}
-
-void
-FFTFileCache::setColumnAt(size_t x, float *mags, float *phases, float factor)
-{
-    size_t h = getHeight();
-    for (size_t y = 0; y < h; ++y) {
-        m_colbuf[y * 2] = mags[y];
-        m_colbuf[y * 2 + 1] = phases[y];
-    }
-    m_colbuf[h * 2] = factor;
-    m_mfc->setColumnAt(x, m_colbuf);
-}
-
--- a/base/FFTFileCache.h	Fri May 05 12:34:51 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,66 +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_FILE_CACHE_H_
-#define _FFT_FILE_CACHE_H_
-
-#include "FFTCache.h"
-#include "MatrixFile.h"
-
-class FFTFileCache : public FFTCacheBase
-{
-public:
-    //!!! This is very much a work in progress.
-    //
-    // Initially, make this take a string for the filename,
-    // and make the spectrogram layer have two, one for the main
-    // thread and one for the fill thread, one RO and one RW, both
-    // using the same string based off spectrogram layer address
-    // or export ID.
-    // Subsequently factor out into reader and writer;
-    // make take arguments to ctor describing FFT parameters and
-    // calculate its own string and eventually do its own FFT as
-    // well.  Intention is to make it able ultimately to write
-    // its own cache so it can do it in the background while e.g.
-    // plugins get data from it -- need the reader thread to be able
-    // to block waiting for the writer thread as appropriate.
-
-    FFTFileCache(QString fileBase, MatrixFile::Mode mode);
-    virtual ~FFTFileCache();
-
-    virtual size_t getWidth() const;
-    virtual size_t getHeight() const;
-	
-    virtual void resize(size_t width, size_t height);
-    virtual void reset(); // zero-fill or 1-fill as appropriate without changing size
-	
-    virtual float getMagnitudeAt(size_t x, size_t y) const;
-    virtual float getNormalizedMagnitudeAt(size_t x, size_t y) const;
-    virtual float getPhaseAt(size_t x, size_t y) const;
-
-    virtual void setNormalizationFactor(size_t x, float factor);
-    virtual void setMagnitudeAt(size_t x, size_t y, float mag);
-    virtual void setNormalizedMagnitudeAt(size_t x, size_t y, float norm);
-    virtual void setPhaseAt(size_t x, size_t y, float phase);
-
-    //!!! not thread safe (but then neither is m_mfc)
-    virtual void setColumnAt(size_t x, float *mags, float *phases, float factor);
-
-protected:
-    float *m_colbuf;
-    MatrixFile *m_mfc;
-};
-
-#endif
--- a/base/FileReadThread.cpp	Fri May 05 12:34:51 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,274 +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 "FileReadThread.h"
-
-#include <iostream>
-#include <unistd.h>
-
-FileReadThread::FileReadThread() :
-    m_nextToken(0),
-    m_exiting(false)
-{
-}
-
-void
-FileReadThread::run()
-{
-    m_mutex.lock();
-
-    while (!m_exiting) {
-        if (m_queue.empty()) {
-            m_condition.wait(&m_mutex, 1000);
-        } else {
-            process();
-        }
-        notifyCancelled();
-    }
-
-    notifyCancelled();
-    m_mutex.unlock();
-
-    std::cerr << "FileReadThread::run() exiting" << std::endl;
-}
-
-void
-FileReadThread::finish()
-{
-    std::cerr << "FileReadThread::finish()" << std::endl;
-
-    m_mutex.lock();
-    while (!m_queue.empty()) {
-        m_cancelledRequests[m_queue.begin()->first] = m_queue.begin()->second;
-        m_newlyCancelled.insert(m_queue.begin()->first);
-        m_queue.erase(m_queue.begin());
-    }
-
-    m_exiting = true;
-    m_mutex.unlock();
-
-    m_condition.wakeAll();
-
-    std::cerr << "FileReadThread::finish() exiting" << std::endl;
-}
-
-int
-FileReadThread::request(const Request &request)
-{
-    m_mutex.lock();
-    
-    int token = m_nextToken++;
-    m_queue[token] = request;
-
-    m_mutex.unlock();
-    m_condition.wakeAll();
-
-    return token;
-}
-
-void
-FileReadThread::cancel(int token)
-{
-    m_mutex.lock();
-
-    if (m_queue.find(token) != m_queue.end()) {
-        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
-FileReadThread::isReady(int token)
-{
-    m_mutex.lock();
-
-    bool ready = m_readyRequests.find(token) != m_readyRequests.end();
-
-    m_mutex.unlock();
-    return ready;
-}
-
-bool
-FileReadThread::isCancelled(int token)
-{
-    m_mutex.lock();
-
-    bool cancelled = 
-        m_cancelledRequests.find(token) != m_cancelledRequests.end() &&
-        m_newlyCancelled.find(token) == m_newlyCancelled.end();
-
-    m_mutex.unlock();
-    return cancelled;
-}
-
-bool
-FileReadThread::getRequest(int token, Request &request)
-{
-    m_mutex.lock();
-
-    bool found = false;
-
-    if (m_queue.find(token) != m_queue.end()) {
-        request = m_queue[token];
-        found = true;
-    } else if (m_cancelledRequests.find(token) != m_cancelledRequests.end()) {
-        request = m_cancelledRequests[token];
-        found = true;
-    } else if (m_readyRequests.find(token) != m_readyRequests.end()) {
-        request = m_readyRequests[token];
-        found = true;
-    }
-
-    m_mutex.unlock();
-    
-    return found;
-}
-
-void
-FileReadThread::done(int token)
-{
-    m_mutex.lock();
-
-    bool found = false;
-
-    if (m_cancelledRequests.find(token) != m_cancelledRequests.end()) {
-        m_cancelledRequests.erase(token);
-        m_newlyCancelled.erase(token);
-        found = true;
-    } else if (m_readyRequests.find(token) != m_readyRequests.end()) {
-        m_readyRequests.erase(token);
-        found = true;
-    } else if (m_queue.find(token) != m_queue.end()) {
-        std::cerr << "WARNING: FileReadThread::done(" << token << "): request is still in queue (wait or cancel it)" << std::endl;
-    }
-
-    m_mutex.unlock();
-
-    if (!found) {
-        std::cerr << "WARNING: FileReadThread::done(" << token << "): request not found" << std::endl;
-    }
-}
-
-void
-FileReadThread::process()
-{
-    // entered with m_mutex locked and m_queue non-empty
-
-    int token = m_queue.begin()->first;
-    Request request = m_queue.begin()->second;
-
-    m_mutex.unlock();
-
-    std::cerr << "FileReadThread::process: got something to do" << std::endl;
-
-    bool successful = false;
-    bool seekFailed = false;
-    ssize_t r = 0;
-
-    if (request.mutex) request.mutex->lock();
-
-    if (::lseek(request.fd, request.start, SEEK_SET) == (off_t)-1) {
-        seekFailed = true;
-    } else {
-        
-        // 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();
-
-    if (seekFailed) {
-        ::perror("Seek failed");
-        std::cerr << "ERROR: FileReadThread::process: seek to "
-                  << request.start << " failed" << std::endl;
-        request.size = 0;
-    } else {
-        if (r < 0) {
-            ::perror("ERROR: FileReadThread::process: Read failed");
-            request.size = 0;
-        } else if (r < ssize_t(request.size)) {
-            std::cerr << "WARNING: FileReadThread::process: read "
-                      << request.size << " returned only " << r << " bytes"
-                      << std::endl;
-            request.size = r;
-        } else {
-            successful = true;
-        }
-    }
-        
-    // Check that the token hasn't been cancelled and the thread
-    // hasn't been asked to finish
-    
-    m_mutex.lock();
-        
-    if (m_queue.find(token) != m_queue.end() && !m_exiting) {
-        m_queue.erase(token);
-        m_readyRequests[token] = request;
-        m_mutex.unlock();
-        std::cerr << "emitting" << std::endl;
-        emit ready(token, successful);
-        m_mutex.lock();
-    }
-}
-
-void
-FileReadThread::notifyCancelled()
-{
-    // 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	Fri May 05 12:34:51 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,76 +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 _FILE_READ_THREAD_H_
-#define _FILE_READ_THREAD_H_
-
-#include "NonRTThread.h"
-
-#include <QMutex>
-#include <QWaitCondition>
-
-#include <map>
-#include <set>
-
-#include <stdint.h>
-
-class FileReadThread : public NonRTThread
-{
-    Q_OBJECT
-
-public:
-    FileReadThread();
-
-    virtual void run();
-    virtual void finish();
-
-    struct Request {
-        int fd;
-        QMutex *mutex; // used to synchronise access to fd; may be null
-        off_t start;
-        size_t size;
-        char *data; // caller is responsible for allocating and deallocating
-    };
-    
-    virtual int request(const Request &request);
-    virtual void cancel(int token);
-
-    virtual bool isReady(int token);
-    virtual bool isCancelled(int token); // and safe to delete
-    virtual bool getRequest(int token, Request &request);
-    virtual void done(int token);
-
-signals:
-    void ready(int token, bool successful); 
-    void cancelled(int token);
-    
-protected:
-    int m_nextToken;
-    bool m_exiting;
-    
-    typedef std::map<int, Request> RequestQueue;
-    RequestQueue m_queue;
-    RequestQueue m_cancelledRequests;
-    RequestQueue m_readyRequests;
-    std::set<int> m_newlyCancelled;
-
-    QMutex m_mutex;
-    QWaitCondition m_condition;
-
-    void process();
-    void notifyCancelled();
-};
-
-#endif
--- a/base/MatrixFile.cpp	Fri May 05 12:34:51 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,470 +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 "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(512),
-    m_prevX(0),
-    m_requestToken(-1),
-    m_spareData(0)
-{
-    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;
-
-    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) free(requestData);
-    if (m_cache.data) free(m_cache.data);
-    if (m_spareData) free(m_spareData);
-
-    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) {
-                    if (m_spareData) free(m_spareData);
-                    m_spareData = (char *)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);
-        }
-
-        if (m_spareData) free(m_spareData);
-        m_spareData = 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 *)realloc(m_spareData, rw * m_height * sizeof(float));
-    MUNLOCK(request.data, rw * m_height * sizeof(float));
-    m_spareData = 0;
-
-    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;
-}
-
-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;
-}
-
--- a/base/MatrixFile.h	Fri May 05 12:34:51 2006 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +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 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:
-    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;
-    char *m_spareData;
-
-    static std::map<QString, int> m_refcount;
-    static QMutex m_refcountMutex;
-    QMutex m_fdMutex;
-    QMutex m_cacheMutex;
-};
-
-#endif
-
--- a/base/View.cpp	Fri May 05 12:34:51 2006 +0000
+++ b/base/View.cpp	Fri May 05 13:06:47 2006 +0000
@@ -739,8 +739,13 @@
     if (!visible) return;
 
     bool modifierPressed = // we only care about these ones
-	((QApplication::keyboardModifiers() & Qt::ShiftModifier) ||
-	 (QApplication::keyboardModifiers() & Qt::ControlModifier));
+	((QApplication::keyboardModifiers() & Qt::ShiftModifier)
+#ifndef Q_WS_MAC
+         /* OS/X reports that CtrlModifier is pressed after we've
+            imported a file with Apple+I even though we then released it */
+         || (QApplication::keyboardModifiers() & Qt::ControlModifier)
+#endif
+            );
 
     switch (m_followPlay) {
 
--- a/transform/TransformFactory.cpp	Fri May 05 12:34:51 2006 +0000
+++ b/transform/TransformFactory.cpp	Fri May 05 13:06:47 2006 +0000
@@ -175,7 +175,7 @@
                                  !plugin->getParameterDescriptors().empty());
 
 	    transforms[transformName] = 
-                TransformDesc(tr("Analysis Plugin"),
+                TransformDesc(tr("Analysis Plugins"),
                               transformName,
                               userDescription,
                               friendlyName,
@@ -255,7 +255,7 @@
             bool configurable = (descriptor->parameterCount > 0);
 
 	    transforms[transformName] = 
-                TransformDesc(tr("Real-Time Plugin"),
+                TransformDesc(tr("Other Plugins"),
                               transformName,
                               userDescription,
                               userDescription,