changeset 203:0de89bce24e4

* Move some things around to facilitate plundering libraries for other applications without needing to duplicate so much code. sv/osc -> data/osc sv/audioio -> audioio sv/transform -> plugin/transform sv/document -> document (will rename to framework in next commit)
author Chris Cannam
date Wed, 24 Oct 2007 16:34:31 +0000
parents 738968d96e19
children 688604a2c038
files audioio/AudioCallbackPlaySource.cpp audioio/AudioCallbackPlaySource.h audioio/AudioCallbackPlayTarget.cpp audioio/AudioCallbackPlayTarget.h audioio/AudioCoreAudioTarget.cpp audioio/AudioCoreAudioTarget.h audioio/AudioGenerator.cpp audioio/AudioGenerator.h audioio/AudioJACKTarget.cpp audioio/AudioJACKTarget.h audioio/AudioPortAudioTarget.cpp audioio/AudioPortAudioTarget.h audioio/AudioTargetFactory.cpp audioio/AudioTargetFactory.h audioio/PhaseVocoderTimeStretcher.cpp audioio/PhaseVocoderTimeStretcher.h audioio/PlaySpeedRangeMapper.cpp audioio/PlaySpeedRangeMapper.h document/Document.cpp document/Document.h document/SVFileReader.cpp document/SVFileReader.h main/MainWindow.cpp main/MainWindow.h main/MainWindowBase.cpp main/MainWindowBase.h osc/OSCMessage.cpp osc/OSCMessage.h osc/OSCQueue.cpp osc/OSCQueue.h osc/demoscript.sh osc/sv-command osc/sv-osc-send.c sv.pro transform/FeatureExtractionPluginTransform.cpp transform/FeatureExtractionPluginTransform.h transform/PluginTransform.cpp transform/PluginTransform.h transform/RealTimePluginTransform.cpp transform/RealTimePluginTransform.h transform/Transform.cpp transform/Transform.h transform/TransformFactory.cpp transform/TransformFactory.h
diffstat 44 files changed, 14 insertions(+), 13254 deletions(-) [+]
line wrap: on
line diff
--- a/audioio/AudioCallbackPlaySource.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1493 +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 "AudioCallbackPlaySource.h"
-
-#include "AudioGenerator.h"
-
-#include "data/model/Model.h"
-#include "view/ViewManager.h"
-#include "base/PlayParameterRepository.h"
-#include "base/Preferences.h"
-#include "data/model/DenseTimeValueModel.h"
-#include "data/model/WaveFileModel.h"
-#include "data/model/SparseOneDimensionalModel.h"
-#include "plugin/RealTimePluginInstance.h"
-#include "PhaseVocoderTimeStretcher.h"
-
-#include <iostream>
-#include <cassert>
-
-//#define DEBUG_AUDIO_PLAY_SOURCE 1
-//#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1
-
-const size_t AudioCallbackPlaySource::m_ringBufferSize = 131071;
-
-AudioCallbackPlaySource::AudioCallbackPlaySource(ViewManager *manager) :
-    m_viewManager(manager),
-    m_audioGenerator(new AudioGenerator()),
-    m_readBuffers(0),
-    m_writeBuffers(0),
-    m_readBufferFill(0),
-    m_writeBufferFill(0),
-    m_bufferScavenger(1),
-    m_sourceChannelCount(0),
-    m_blockSize(1024),
-    m_sourceSampleRate(0),
-    m_targetSampleRate(0),
-    m_playLatency(0),
-    m_playing(false),
-    m_exiting(false),
-    m_lastModelEndFrame(0),
-    m_outputLeft(0.0),
-    m_outputRight(0.0),
-    m_auditioningPlugin(0),
-    m_auditioningPluginBypassed(false),
-    m_timeStretcher(0),
-    m_fillThread(0),
-    m_converter(0),
-    m_crapConverter(0),
-    m_resampleQuality(Preferences::getInstance()->getResampleQuality())
-{
-    m_viewManager->setAudioPlaySource(this);
-
-    connect(m_viewManager, SIGNAL(selectionChanged()),
-	    this, SLOT(selectionChanged()));
-    connect(m_viewManager, SIGNAL(playLoopModeChanged()),
-	    this, SLOT(playLoopModeChanged()));
-    connect(m_viewManager, SIGNAL(playSelectionModeChanged()),
-	    this, SLOT(playSelectionModeChanged()));
-
-    connect(PlayParameterRepository::getInstance(),
-	    SIGNAL(playParametersChanged(PlayParameters *)),
-	    this, SLOT(playParametersChanged(PlayParameters *)));
-
-    connect(Preferences::getInstance(),
-            SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
-            this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
-}
-
-AudioCallbackPlaySource::~AudioCallbackPlaySource()
-{
-    m_exiting = true;
-
-    if (m_fillThread) {
-	m_condition.wakeAll();
-	m_fillThread->wait();
-	delete m_fillThread;
-    }
-
-    clearModels();
-    
-    if (m_readBuffers != m_writeBuffers) {
-	delete m_readBuffers;
-    }
-
-    delete m_writeBuffers;
-
-    delete m_audioGenerator;
-
-    m_bufferScavenger.scavenge(true);
-    m_pluginScavenger.scavenge(true);
-    m_timeStretcherScavenger.scavenge(true);
-}
-
-void
-AudioCallbackPlaySource::addModel(Model *model)
-{
-    if (m_models.find(model) != m_models.end()) return;
-
-    bool canPlay = m_audioGenerator->addModel(model);
-
-    m_mutex.lock();
-
-    m_models.insert(model);
-    if (model->getEndFrame() > m_lastModelEndFrame) {
-	m_lastModelEndFrame = model->getEndFrame();
-    }
-
-    bool buffersChanged = false, srChanged = false;
-
-    size_t modelChannels = 1;
-    DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
-    if (dtvm) modelChannels = dtvm->getChannelCount();
-    if (modelChannels > m_sourceChannelCount) {
-	m_sourceChannelCount = modelChannels;
-    }
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "Adding model with " << modelChannels << " channels " << std::endl;
-#endif
-
-    if (m_sourceSampleRate == 0) {
-
-	m_sourceSampleRate = model->getSampleRate();
-	srChanged = true;
-
-    } else if (model->getSampleRate() != m_sourceSampleRate) {
-
-        // If this is a dense time-value model and we have no other, we
-        // can just switch to this model's sample rate
-
-        if (dtvm) {
-
-            bool conflicting = false;
-
-            for (std::set<Model *>::const_iterator i = m_models.begin();
-                 i != m_models.end(); ++i) {
-                // Only wave file models can be considered conflicting --
-                // writable wave file models are derived and we shouldn't
-                // take their rates into account.  Also, don't give any
-                // particular weight to a file that's already playing at
-                // the wrong rate anyway
-                WaveFileModel *wfm = dynamic_cast<WaveFileModel *>(*i);
-                if (wfm && wfm != dtvm &&
-                    wfm->getSampleRate() != model->getSampleRate() &&
-                    wfm->getSampleRate() == m_sourceSampleRate) {
-                    std::cerr << "AudioCallbackPlaySource::addModel: Conflicting wave file model " << *i << " found" << std::endl;
-                    conflicting = true;
-                    break;
-                }
-            }
-
-            if (conflicting) {
-
-                std::cerr << "AudioCallbackPlaySource::addModel: ERROR: "
-                          << "New model sample rate does not match" << std::endl
-                          << "existing model(s) (new " << model->getSampleRate()
-                          << " vs " << m_sourceSampleRate
-                          << "), playback will be wrong"
-                          << std::endl;
-                
-                emit sampleRateMismatch(model->getSampleRate(),
-                                        m_sourceSampleRate,
-                                        false);
-            } else {
-                m_sourceSampleRate = model->getSampleRate();
-                srChanged = true;
-            }
-        }
-    }
-
-    if (!m_writeBuffers || (m_writeBuffers->size() < getTargetChannelCount())) {
-	clearRingBuffers(true, getTargetChannelCount());
-	buffersChanged = true;
-    } else {
-	if (canPlay) clearRingBuffers(true);
-    }
-
-    if (buffersChanged || srChanged) {
-	if (m_converter) {
-	    src_delete(m_converter);
-            src_delete(m_crapConverter);
-	    m_converter = 0;
-            m_crapConverter = 0;
-	}
-    }
-
-    m_mutex.unlock();
-
-    m_audioGenerator->setTargetChannelCount(getTargetChannelCount());
-
-    if (!m_fillThread) {
-	m_fillThread = new FillThread(*this);
-	m_fillThread->start();
-    }
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s) -- emitting modelReplaced" << std::endl;
-#endif
-
-    if (buffersChanged || srChanged) {
-	emit modelReplaced();
-    }
-
-    connect(model, SIGNAL(modelChanged(size_t, size_t)),
-            this, SLOT(modelChanged(size_t, size_t)));
-
-    m_condition.wakeAll();
-}
-
-void
-AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame)
-{
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cerr << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << std::endl;
-#endif
-    if (endFrame > m_lastModelEndFrame) m_lastModelEndFrame = endFrame;
-}
-
-void
-AudioCallbackPlaySource::removeModel(Model *model)
-{
-    m_mutex.lock();
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "AudioCallbackPlaySource::removeModel(" << model << ")" << std::endl;
-#endif
-
-    disconnect(model, SIGNAL(modelChanged(size_t, size_t)),
-               this, SLOT(modelChanged(size_t, size_t)));
-
-    m_models.erase(model);
-
-    if (m_models.empty()) {
-	if (m_converter) {
-	    src_delete(m_converter);
-            src_delete(m_crapConverter);
-	    m_converter = 0;
-            m_crapConverter = 0;
-	}
-	m_sourceSampleRate = 0;
-    }
-
-    size_t lastEnd = 0;
-    for (std::set<Model *>::const_iterator i = m_models.begin();
-	 i != m_models.end(); ++i) {
-//	std::cout << "AudioCallbackPlaySource::removeModel(" << model << "): checking end frame on model " << *i << std::endl;
-	if ((*i)->getEndFrame() > lastEnd) lastEnd = (*i)->getEndFrame();
-//	std::cout << "(done, lastEnd now " << lastEnd << ")" << std::endl;
-    }
-    m_lastModelEndFrame = lastEnd;
-
-    m_mutex.unlock();
-
-    m_audioGenerator->removeModel(model);
-
-    clearRingBuffers();
-}
-
-void
-AudioCallbackPlaySource::clearModels()
-{
-    m_mutex.lock();
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "AudioCallbackPlaySource::clearModels()" << std::endl;
-#endif
-
-    m_models.clear();
-
-    if (m_converter) {
-	src_delete(m_converter);
-        src_delete(m_crapConverter);
-	m_converter = 0;
-        m_crapConverter = 0;
-    }
-
-    m_lastModelEndFrame = 0;
-
-    m_sourceSampleRate = 0;
-
-    m_mutex.unlock();
-
-    m_audioGenerator->clearModels();
-}    
-
-void
-AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count)
-{
-    if (!haveLock) m_mutex.lock();
-
-    if (count == 0) {
-	if (m_writeBuffers) count = m_writeBuffers->size();
-    }
-
-    size_t sf = m_readBufferFill;
-    RingBuffer<float> *rb = getReadRingBuffer(0);
-    if (rb) {
-	//!!! This is incorrect if we're in a non-contiguous selection
-	//Same goes for all related code (subtracting the read space
-	//from the fill frame to try to establish where the effective
-	//pre-resample/timestretch read pointer is)
-	size_t rs = rb->getReadSpace();
-	if (rs < sf) sf -= rs;
-	else sf = 0;
-    }
-    m_writeBufferFill = sf;
-
-    if (m_readBuffers != m_writeBuffers) {
-	delete m_writeBuffers;
-    }
-
-    m_writeBuffers = new RingBufferVector;
-
-    for (size_t i = 0; i < count; ++i) {
-	m_writeBuffers->push_back(new RingBuffer<float>(m_ringBufferSize));
-    }
-
-//    std::cout << "AudioCallbackPlaySource::clearRingBuffers: Created "
-//	      << count << " write buffers" << std::endl;
-
-    if (!haveLock) {
-	m_mutex.unlock();
-    }
-}
-
-void
-AudioCallbackPlaySource::play(size_t startFrame)
-{
-    if (m_viewManager->getPlaySelectionMode() &&
-	!m_viewManager->getSelections().empty()) {
-	MultiSelection::SelectionList selections = m_viewManager->getSelections();
-	MultiSelection::SelectionList::iterator i = selections.begin();
-	if (i != selections.end()) {
-	    if (startFrame < i->getStartFrame()) {
-		startFrame = i->getStartFrame();
-	    } else {
-		MultiSelection::SelectionList::iterator j = selections.end();
-		--j;
-		if (startFrame >= j->getEndFrame()) {
-		    startFrame = i->getStartFrame();
-		}
-	    }
-	}
-    } else {
-	if (startFrame >= m_lastModelEndFrame) {
-	    startFrame = 0;
-	}
-    }
-
-    // The fill thread will automatically empty its buffers before
-    // starting again if we have not so far been playing, but not if
-    // we're just re-seeking.
-
-    m_mutex.lock();
-    if (m_playing) {
-	m_readBufferFill = m_writeBufferFill = startFrame;
-	if (m_readBuffers) {
-	    for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-		RingBuffer<float> *rb = getReadRingBuffer(c);
-		if (rb) rb->reset();
-	    }
-	}
-	if (m_converter) src_reset(m_converter);
-        if (m_crapConverter) src_reset(m_crapConverter);
-    } else {
-	if (m_converter) src_reset(m_converter);
-        if (m_crapConverter) src_reset(m_crapConverter);
-	m_readBufferFill = m_writeBufferFill = startFrame;
-    }
-    m_mutex.unlock();
-
-    m_audioGenerator->reset();
-
-    bool changed = !m_playing;
-    m_playing = true;
-    m_condition.wakeAll();
-    if (changed) emit playStatusChanged(m_playing);
-}
-
-void
-AudioCallbackPlaySource::stop()
-{
-    bool changed = m_playing;
-    m_playing = false;
-    m_condition.wakeAll();
-    if (changed) emit playStatusChanged(m_playing);
-}
-
-void
-AudioCallbackPlaySource::selectionChanged()
-{
-    if (m_viewManager->getPlaySelectionMode()) {
-	clearRingBuffers();
-    }
-}
-
-void
-AudioCallbackPlaySource::playLoopModeChanged()
-{
-    clearRingBuffers();
-}
-
-void
-AudioCallbackPlaySource::playSelectionModeChanged()
-{
-    if (!m_viewManager->getSelections().empty()) {
-	clearRingBuffers();
-    }
-}
-
-void
-AudioCallbackPlaySource::playParametersChanged(PlayParameters *)
-{
-    clearRingBuffers();
-}
-
-void
-AudioCallbackPlaySource::preferenceChanged(PropertyContainer::PropertyName n)
-{
-    if (n == "Resample Quality") {
-        setResampleQuality(Preferences::getInstance()->getResampleQuality());
-    }
-}
-
-void
-AudioCallbackPlaySource::audioProcessingOverload()
-{
-    RealTimePluginInstance *ap = m_auditioningPlugin;
-    if (ap && m_playing && !m_auditioningPluginBypassed) {
-        m_auditioningPluginBypassed = true;
-        emit audioOverloadPluginDisabled();
-    }
-}
-
-void
-AudioCallbackPlaySource::setTargetBlockSize(size_t size)
-{
-//    std::cout << "AudioCallbackPlaySource::setTargetBlockSize() -> " << size << std::endl;
-    assert(size < m_ringBufferSize);
-    m_blockSize = size;
-}
-
-size_t
-AudioCallbackPlaySource::getTargetBlockSize() const
-{
-//    std::cout << "AudioCallbackPlaySource::getTargetBlockSize() -> " << m_blockSize << std::endl;
-    return m_blockSize;
-}
-
-void
-AudioCallbackPlaySource::setTargetPlayLatency(size_t latency)
-{
-    m_playLatency = latency;
-}
-
-size_t
-AudioCallbackPlaySource::getTargetPlayLatency() const
-{
-    return m_playLatency;
-}
-
-size_t
-AudioCallbackPlaySource::getCurrentPlayingFrame()
-{
-    bool resample = false;
-    double ratio = 1.0;
-
-    if (getSourceSampleRate() != getTargetSampleRate()) {
-	resample = true;
-	ratio = double(getSourceSampleRate()) / double(getTargetSampleRate());
-    }
-
-    size_t readSpace = 0;
-    for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-	RingBuffer<float> *rb = getReadRingBuffer(c);
-	if (rb) {
-	    size_t spaceHere = rb->getReadSpace();
-	    if (c == 0 || spaceHere < readSpace) readSpace = spaceHere;
-	}
-    }
-
-    if (resample) {
-	readSpace = size_t(readSpace * ratio + 0.1);
-    }
-
-    size_t latency = m_playLatency;
-    if (resample) latency = size_t(m_playLatency * ratio + 0.1);
-
-    PhaseVocoderTimeStretcher *timeStretcher = m_timeStretcher;
-    if (timeStretcher) {
-	latency += timeStretcher->getProcessingLatency();
-    }
-
-    latency += readSpace;
-    size_t bufferedFrame = m_readBufferFill;
-
-    bool looping = m_viewManager->getPlayLoopMode();
-    bool constrained = (m_viewManager->getPlaySelectionMode() &&
-			!m_viewManager->getSelections().empty());
-
-    size_t framePlaying = bufferedFrame;
-
-    if (looping && !constrained) {
-	while (framePlaying < latency) framePlaying += m_lastModelEndFrame;
-    }
-
-    if (framePlaying > latency) framePlaying -= latency;
-    else framePlaying = 0;
-
-    if (!constrained) {
-	if (!looping && framePlaying > m_lastModelEndFrame) {
-	    framePlaying = m_lastModelEndFrame;
-	    stop();
-	}
-	return framePlaying;
-    }
-
-    MultiSelection::SelectionList selections = m_viewManager->getSelections();
-    MultiSelection::SelectionList::const_iterator i;
-
-//    i = selections.begin();
-//    size_t rangeStart = i->getStartFrame();
-
-    i = selections.end();
-    --i;
-    size_t rangeEnd = i->getEndFrame();
-
-    for (i = selections.begin(); i != selections.end(); ++i) {
-	if (i->contains(bufferedFrame)) break;
-    }
-
-    size_t f = bufferedFrame;
-
-//    std::cout << "getCurrentPlayingFrame: f=" << f << ", latency=" << latency << ", rangeEnd=" << rangeEnd << std::endl;
-
-    if (i == selections.end()) {
-	--i;
-	if (i->getEndFrame() + latency < f) {
-//    std::cout << "framePlaying = " << framePlaying << ", rangeEnd = " << rangeEnd << std::endl;
-
-	    if (!looping && (framePlaying > rangeEnd)) {
-//		std::cout << "STOPPING" << std::endl;
-		stop();
-		return rangeEnd;
-	    } else {
-		return framePlaying;
-	    }
-	} else {
-//	    std::cout << "latency <- " << latency << "-(" << f << "-" << i->getEndFrame() << ")" << std::endl;
-	    latency -= (f - i->getEndFrame());
-	    f = i->getEndFrame();
-	}
-    }
-
-//    std::cout << "i=(" << i->getStartFrame() << "," << i->getEndFrame() << ") f=" << f << ", latency=" << latency << std::endl;
-
-    while (latency > 0) {
-	size_t offset = f - i->getStartFrame();
-	if (offset >= latency) {
-	    if (f > latency) {
-		framePlaying = f - latency;
-	    } else {
-		framePlaying = 0;
-	    }
-	    break;
-	} else {
-	    if (i == selections.begin()) {
-		if (looping) {
-		    i = selections.end();
-		}
-	    }
-	    latency -= offset;
-	    --i;
-	    f = i->getEndFrame();
-	}
-    }
-
-    return framePlaying;
-}
-
-void
-AudioCallbackPlaySource::setOutputLevels(float left, float right)
-{
-    m_outputLeft = left;
-    m_outputRight = right;
-}
-
-bool
-AudioCallbackPlaySource::getOutputLevels(float &left, float &right)
-{
-    left = m_outputLeft;
-    right = m_outputRight;
-    return true;
-}
-
-void
-AudioCallbackPlaySource::setTargetSampleRate(size_t sr)
-{
-    m_targetSampleRate = sr;
-    initialiseConverter();
-}
-
-void
-AudioCallbackPlaySource::initialiseConverter()
-{
-    m_mutex.lock();
-
-    if (m_converter) {
-        src_delete(m_converter);
-        src_delete(m_crapConverter);
-        m_converter = 0;
-        m_crapConverter = 0;
-    }
-
-    if (getSourceSampleRate() != getTargetSampleRate()) {
-
-	int err = 0;
-
-	m_converter = src_new(m_resampleQuality == 2 ? SRC_SINC_BEST_QUALITY :
-                              m_resampleQuality == 1 ? SRC_SINC_MEDIUM_QUALITY :
-                              m_resampleQuality == 0 ? SRC_SINC_FASTEST :
-                                                       SRC_SINC_MEDIUM_QUALITY,
-			      getTargetChannelCount(), &err);
-
-        if (m_converter) {
-            m_crapConverter = src_new(SRC_LINEAR,
-                                      getTargetChannelCount(),
-                                      &err);
-        }
-
-	if (!m_converter || !m_crapConverter) {
-	    std::cerr
-		<< "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: "
-		<< src_strerror(err) << std::endl;
-
-            if (m_converter) {
-                src_delete(m_converter);
-                m_converter = 0;
-            } 
-
-            if (m_crapConverter) {
-                src_delete(m_crapConverter);
-                m_crapConverter = 0;
-            }
-
-            m_mutex.unlock();
-
-            emit sampleRateMismatch(getSourceSampleRate(),
-                                    getTargetSampleRate(),
-                                    false);
-	} else {
-
-            m_mutex.unlock();
-
-            emit sampleRateMismatch(getSourceSampleRate(),
-                                    getTargetSampleRate(),
-                                    true);
-        }
-    } else {
-        m_mutex.unlock();
-    }
-}
-
-void
-AudioCallbackPlaySource::setResampleQuality(int q)
-{
-    if (q == m_resampleQuality) return;
-    m_resampleQuality = q;
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cerr << "AudioCallbackPlaySource::setResampleQuality: setting to "
-              << m_resampleQuality << std::endl;
-#endif
-
-    initialiseConverter();
-}
-
-void
-AudioCallbackPlaySource::setAuditioningPlugin(RealTimePluginInstance *plugin)
-{
-    RealTimePluginInstance *formerPlugin = m_auditioningPlugin;
-    m_auditioningPlugin = plugin;
-    m_auditioningPluginBypassed = false;
-    if (formerPlugin) m_pluginScavenger.claim(formerPlugin);
-}
-
-void
-AudioCallbackPlaySource::setSoloModelSet(std::set<Model *> s)
-{
-    m_audioGenerator->setSoloModelSet(s);
-    clearRingBuffers();
-}
-
-void
-AudioCallbackPlaySource::clearSoloModelSet()
-{
-    m_audioGenerator->clearSoloModelSet();
-    clearRingBuffers();
-}
-
-size_t
-AudioCallbackPlaySource::getTargetSampleRate() const
-{
-    if (m_targetSampleRate) return m_targetSampleRate;
-    else return getSourceSampleRate();
-}
-
-size_t
-AudioCallbackPlaySource::getSourceChannelCount() const
-{
-    return m_sourceChannelCount;
-}
-
-size_t
-AudioCallbackPlaySource::getTargetChannelCount() const
-{
-    if (m_sourceChannelCount < 2) return 2;
-    return m_sourceChannelCount;
-}
-
-size_t
-AudioCallbackPlaySource::getSourceSampleRate() const
-{
-    return m_sourceSampleRate;
-}
-
-void
-AudioCallbackPlaySource::setTimeStretch(float factor, bool sharpen, bool mono)
-{
-    // Avoid locks -- create, assign, mark old one for scavenging
-    // later (as a call to getSourceSamples may still be using it)
-
-    PhaseVocoderTimeStretcher *existingStretcher = m_timeStretcher;
-
-    size_t channels = getTargetChannelCount();
-    if (mono) channels = 1;
-
-    if (existingStretcher &&
-        existingStretcher->getRatio() == factor &&
-        existingStretcher->getSharpening() == sharpen &&
-        existingStretcher->getChannelCount() == channels) {
-	return;
-    }
-
-    if (factor != 1) {
-
-        if (existingStretcher &&
-            existingStretcher->getSharpening() == sharpen &&
-            existingStretcher->getChannelCount() == channels) {
-            existingStretcher->setRatio(factor);
-            return;
-        }
-
-	PhaseVocoderTimeStretcher *newStretcher = new PhaseVocoderTimeStretcher
-	    (getTargetSampleRate(),
-             channels,
-             factor,
-             sharpen,
-             getTargetBlockSize());
-
-	m_timeStretcher = newStretcher;
-
-    } else {
-	m_timeStretcher = 0;
-    }
-
-    if (existingStretcher) {
-	m_timeStretcherScavenger.claim(existingStretcher);
-    }
-}
-
-size_t
-AudioCallbackPlaySource::getSourceSamples(size_t count, float **buffer)
-{
-    if (!m_playing) {
-	for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
-	    for (size_t i = 0; i < count; ++i) {
-		buffer[ch][i] = 0.0;
-	    }
-	}
-	return 0;
-    }
-
-    // Ensure that all buffers have at least the amount of data we
-    // need -- else reduce the size of our requests correspondingly
-
-    for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
-
-        RingBuffer<float> *rb = getReadRingBuffer(ch);
-        
-        if (!rb) {
-            std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: "
-                      << "No ring buffer available for channel " << ch
-                      << ", returning no data here" << std::endl;
-            count = 0;
-            break;
-        }
-
-        size_t rs = rb->getReadSpace();
-        if (rs < count) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            std::cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: "
-                      << "Ring buffer for channel " << ch << " has only "
-                      << rs << " (of " << count << ") samples available, "
-                      << "reducing request size" << std::endl;
-#endif
-            count = rs;
-        }
-    }
-
-    if (count == 0) return 0;
-
-    PhaseVocoderTimeStretcher *ts = m_timeStretcher;
-
-    if (!ts || ts->getRatio() == 1) {
-
-	size_t got = 0;
-
-	for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
-
-	    RingBuffer<float> *rb = getReadRingBuffer(ch);
-
-	    if (rb) {
-
-		// this is marginally more likely to leave our channels in
-		// sync after a processing failure than just passing "count":
-		size_t request = count;
-		if (ch > 0) request = got;
-
-		got = rb->read(buffer[ch], request);
-	    
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-		std::cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << std::endl;
-#endif
-	    }
-
-	    for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
-		for (size_t i = got; i < count; ++i) {
-		    buffer[ch][i] = 0.0;
-		}
-	    }
-	}
-
-        applyAuditioningEffect(count, buffer);
-
-        m_condition.wakeAll();
-	return got;
-    }
-
-    float ratio = ts->getRatio();
-
-//            std::cout << "ratio = " << ratio << std::endl;
-
-    size_t channels = getTargetChannelCount();
-    bool mix = (channels > 1 && ts->getChannelCount() == 1);
-
-    size_t available;
-
-    int warned = 0;
-
-    // We want output blocks of e.g. 1024 (probably fixed, certainly
-    // bounded).  We can provide input blocks of any size (unbounded)
-    // at the timestretcher's request.  The input block for a given
-    // output is approx output / ratio, but we can't predict it
-    // exactly, for an adaptive timestretcher.  The stretcher will
-    // need some additional buffer space.  See the time stretcher code
-    // and comments.
-
-    while ((available = ts->getAvailableOutputSamples()) < count) {
-
-        size_t reqd = lrintf((count - available) / ratio);
-        reqd = std::max(reqd, ts->getRequiredInputSamples());
-        if (reqd == 0) reqd = 1;
-                
-        float *ib[channels];
-
-        size_t got = reqd;
-
-        if (mix) {
-            for (size_t c = 0; c < channels; ++c) {
-                if (c == 0) ib[c] = new float[reqd]; //!!! fix -- this is a rt function
-                else ib[c] = 0;
-                RingBuffer<float> *rb = getReadRingBuffer(c);
-                if (rb) {
-                    size_t gotHere;
-                    if (c > 0) gotHere = rb->readAdding(ib[0], got);
-                    else gotHere = rb->read(ib[0], got);
-                    if (gotHere < got) got = gotHere;
-                }
-            }
-        } else {
-            for (size_t c = 0; c < channels; ++c) {
-                ib[c] = new float[reqd]; //!!! fix -- this is a rt function
-                RingBuffer<float> *rb = getReadRingBuffer(c);
-                if (rb) {
-                    size_t gotHere = rb->read(ib[c], got);
-                    if (gotHere < got) got = gotHere;
-                }
-            }
-        }
-
-        if (got < reqd) {
-            std::cerr << "WARNING: Read underrun in playback ("
-                      << got << " < " << reqd << ")" << std::endl;
-        }
-                
-        ts->putInput(ib, got);
-
-        for (size_t c = 0; c < channels; ++c) {
-            delete[] ib[c];
-        }
-
-        if (got == 0) break;
-
-        if (ts->getAvailableOutputSamples() == available) {
-            std::cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << std::endl;
-            if (++warned == 5) break;
-        }
-    }
-
-    ts->getOutput(buffer, count);
-
-    if (mix) {
-        for (size_t c = 1; c < channels; ++c) {
-            for (size_t i = 0; i < count; ++i) {
-                buffer[c][i] = buffer[0][i] / channels;
-            }
-        }
-        for (size_t i = 0; i < count; ++i) {
-            buffer[0][i] /= channels;
-        }
-    }
-
-    applyAuditioningEffect(count, buffer);
-
-    m_condition.wakeAll();
-
-    return count;
-}
-
-void
-AudioCallbackPlaySource::applyAuditioningEffect(size_t count, float **buffers)
-{
-    if (m_auditioningPluginBypassed) return;
-    RealTimePluginInstance *plugin = m_auditioningPlugin;
-    if (!plugin) return;
-
-    if (plugin->getAudioInputCount() != getTargetChannelCount()) {
-//        std::cerr << "plugin input count " << plugin->getAudioInputCount() 
-//                  << " != our channel count " << getTargetChannelCount()
-//                  << std::endl;
-        return;
-    }
-    if (plugin->getAudioOutputCount() != getTargetChannelCount()) {
-//        std::cerr << "plugin output count " << plugin->getAudioOutputCount() 
-//                  << " != our channel count " << getTargetChannelCount()
-//                  << std::endl;
-        return;
-    }
-    if (plugin->getBufferSize() != count) {
-//        std::cerr << "plugin buffer size " << plugin->getBufferSize() 
-//                  << " != our block size " << count
-//                  << std::endl;
-        return;
-    }
-
-    float **ib = plugin->getAudioInputBuffers();
-    float **ob = plugin->getAudioOutputBuffers();
-
-    for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-        for (size_t i = 0; i < count; ++i) {
-            ib[c][i] = buffers[c][i];
-        }
-    }
-
-    plugin->run(Vamp::RealTime::zeroTime);
-    
-    for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-        for (size_t i = 0; i < count; ++i) {
-            buffers[c][i] = ob[c][i];
-        }
-    }
-}    
-
-// Called from fill thread, m_playing true, mutex held
-bool
-AudioCallbackPlaySource::fillBuffers()
-{
-    static float *tmp = 0;
-    static size_t tmpSize = 0;
-
-    size_t space = 0;
-    for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-	RingBuffer<float> *wb = getWriteRingBuffer(c);
-	if (wb) {
-	    size_t spaceHere = wb->getWriteSpace();
-	    if (c == 0 || spaceHere < space) space = spaceHere;
-	}
-    }
-    
-    if (space == 0) return false;
-
-    size_t f = m_writeBufferFill;
-	
-    bool readWriteEqual = (m_readBuffers == m_writeBuffers);
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << std::endl;
-#endif
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "buffered to " << f << " already" << std::endl;
-#endif
-
-    bool resample = (getSourceSampleRate() != getTargetSampleRate());
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << (resample ? "" : "not ") << "resampling (source " << getSourceSampleRate() << ", target " << getTargetSampleRate() << ")" << std::endl;
-#endif
-
-    size_t channels = getTargetChannelCount();
-
-    size_t orig = space;
-    size_t got = 0;
-
-    static float **bufferPtrs = 0;
-    static size_t bufferPtrCount = 0;
-
-    if (bufferPtrCount < channels) {
-	if (bufferPtrs) delete[] bufferPtrs;
-	bufferPtrs = new float *[channels];
-	bufferPtrCount = channels;
-    }
-
-    size_t generatorBlockSize = m_audioGenerator->getBlockSize();
-
-    if (resample && !m_converter) {
-	static bool warned = false;
-	if (!warned) {
-	    std::cerr << "WARNING: sample rates differ, but no converter available!" << std::endl;
-	    warned = true;
-	}
-    }
-
-    if (resample && m_converter) {
-
-	double ratio =
-	    double(getTargetSampleRate()) / double(getSourceSampleRate());
-	orig = size_t(orig / ratio + 0.1);
-
-	// orig must be a multiple of generatorBlockSize
-	orig = (orig / generatorBlockSize) * generatorBlockSize;
-	if (orig == 0) return false;
-
-	size_t work = std::max(orig, space);
-
-	// We only allocate one buffer, but we use it in two halves.
-	// We place the non-interleaved values in the second half of
-	// the buffer (orig samples for channel 0, orig samples for
-	// channel 1 etc), and then interleave them into the first
-	// half of the buffer.  Then we resample back into the second
-	// half (interleaved) and de-interleave the results back to
-	// the start of the buffer for insertion into the ringbuffers.
-	// What a faff -- especially as we've already de-interleaved
-	// the audio data from the source file elsewhere before we
-	// even reach this point.
-	
-	if (tmpSize < channels * work * 2) {
-	    delete[] tmp;
-	    tmp = new float[channels * work * 2];
-	    tmpSize = channels * work * 2;
-	}
-
-	float *nonintlv = tmp + channels * work;
-	float *intlv = tmp;
-	float *srcout = tmp + channels * work;
-	
-	for (size_t c = 0; c < channels; ++c) {
-	    for (size_t i = 0; i < orig; ++i) {
-		nonintlv[channels * i + c] = 0.0f;
-	    }
-	}
-
-	for (size_t c = 0; c < channels; ++c) {
-	    bufferPtrs[c] = nonintlv + c * orig;
-	}
-
-	got = mixModels(f, orig, bufferPtrs);
-
-	// and interleave into first half
-	for (size_t c = 0; c < channels; ++c) {
-	    for (size_t i = 0; i < got; ++i) {
-		float sample = nonintlv[c * got + i];
-		intlv[channels * i + c] = sample;
-	    }
-	}
-		
-	SRC_DATA data;
-	data.data_in = intlv;
-	data.data_out = srcout;
-	data.input_frames = got;
-	data.output_frames = work;
-	data.src_ratio = ratio;
-	data.end_of_input = 0;
-	
-	int err = 0;
-
-        if (m_timeStretcher && m_timeStretcher->getRatio() < 0.4) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            std::cout << "Using crappy converter" << std::endl;
-#endif
-            err = src_process(m_crapConverter, &data);
-        } else {
-            err = src_process(m_converter, &data);
-        }
-
-	size_t toCopy = size_t(got * ratio + 0.1);
-
-	if (err) {
-	    std::cerr
-		<< "AudioCallbackPlaySourceFillThread: ERROR in samplerate conversion: "
-		<< src_strerror(err) << std::endl;
-	    //!!! Then what?
-	} else {
-	    got = data.input_frames_used;
-	    toCopy = data.output_frames_gen;
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	    std::cout << "Resampled " << got << " frames to " << toCopy << " frames" << std::endl;
-#endif
-	}
-	
-	for (size_t c = 0; c < channels; ++c) {
-	    for (size_t i = 0; i < toCopy; ++i) {
-		tmp[i] = srcout[channels * i + c];
-	    }
-	    RingBuffer<float> *wb = getWriteRingBuffer(c);
-	    if (wb) wb->write(tmp, toCopy);
-	}
-
-	m_writeBufferFill = f;
-	if (readWriteEqual) m_readBufferFill = f;
-
-    } else {
-
-	// space must be a multiple of generatorBlockSize
-	space = (space / generatorBlockSize) * generatorBlockSize;
-	if (space == 0) return false;
-
-	if (tmpSize < channels * space) {
-	    delete[] tmp;
-	    tmp = new float[channels * space];
-	    tmpSize = channels * space;
-	}
-
-	for (size_t c = 0; c < channels; ++c) {
-
-	    bufferPtrs[c] = tmp + c * space;
-	    
-	    for (size_t i = 0; i < space; ++i) {
-		tmp[c * space + i] = 0.0f;
-	    }
-	}
-
-	size_t got = mixModels(f, space, bufferPtrs);
-
-	for (size_t c = 0; c < channels; ++c) {
-
-	    RingBuffer<float> *wb = getWriteRingBuffer(c);
-	    if (wb) {
-                size_t actual = wb->write(bufferPtrs[c], got);
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-		std::cout << "Wrote " << actual << " samples for ch " << c << ", now "
-			  << wb->getReadSpace() << " to read" 
-			  << std::endl;
-#endif
-                if (actual < got) {
-                    std::cerr << "WARNING: Buffer overrun in channel " << c
-                              << ": wrote " << actual << " of " << got
-                              << " samples" << std::endl;
-                }
-            }
-	}
-
-	m_writeBufferFill = f;
-	if (readWriteEqual) m_readBufferFill = f;
-
-	//!!! how do we know when ended? need to mark up a fully-buffered flag and check this if we find the buffers empty in getSourceSamples
-    }
-
-    return true;
-}    
-
-size_t
-AudioCallbackPlaySource::mixModels(size_t &frame, size_t count, float **buffers)
-{
-    size_t processed = 0;
-    size_t chunkStart = frame;
-    size_t chunkSize = count;
-    size_t selectionSize = 0;
-    size_t nextChunkStart = chunkStart + chunkSize;
-    
-    bool looping = m_viewManager->getPlayLoopMode();
-    bool constrained = (m_viewManager->getPlaySelectionMode() &&
-			!m_viewManager->getSelections().empty());
-
-    static float **chunkBufferPtrs = 0;
-    static size_t chunkBufferPtrCount = 0;
-    size_t channels = getTargetChannelCount();
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << std::endl;
-#endif
-
-    if (chunkBufferPtrCount < channels) {
-	if (chunkBufferPtrs) delete[] chunkBufferPtrs;
-	chunkBufferPtrs = new float *[channels];
-	chunkBufferPtrCount = channels;
-    }
-
-    for (size_t c = 0; c < channels; ++c) {
-	chunkBufferPtrs[c] = buffers[c];
-    }
-
-    while (processed < count) {
-	
-	chunkSize = count - processed;
-	nextChunkStart = chunkStart + chunkSize;
-	selectionSize = 0;
-
-	size_t fadeIn = 0, fadeOut = 0;
-
-	if (constrained) {
-	    
-	    Selection selection =
-		m_viewManager->getContainingSelection(chunkStart, true);
-	    
-	    if (selection.isEmpty()) {
-		if (looping) {
-		    selection = *m_viewManager->getSelections().begin();
-		    chunkStart = selection.getStartFrame();
-		    fadeIn = 50;
-		}
-	    }
-
-	    if (selection.isEmpty()) {
-
-		chunkSize = 0;
-		nextChunkStart = chunkStart;
-
-	    } else {
-
-		selectionSize =
-		    selection.getEndFrame() -
-		    selection.getStartFrame();
-
-		if (chunkStart < selection.getStartFrame()) {
-		    chunkStart = selection.getStartFrame();
-		    fadeIn = 50;
-		}
-
-		nextChunkStart = chunkStart + chunkSize;
-
-		if (nextChunkStart >= selection.getEndFrame()) {
-		    nextChunkStart = selection.getEndFrame();
-		    fadeOut = 50;
-		}
-
-		chunkSize = nextChunkStart - chunkStart;
-	    }
-	
-	} else if (looping && m_lastModelEndFrame > 0) {
-
-	    if (chunkStart >= m_lastModelEndFrame) {
-		chunkStart = 0;
-	    }
-	    if (chunkSize > m_lastModelEndFrame - chunkStart) {
-		chunkSize = m_lastModelEndFrame - chunkStart;
-	    }
-	    nextChunkStart = chunkStart + chunkSize;
-	}
-	
-//	std::cout << "chunkStart " << chunkStart << ", chunkSize " << chunkSize << ", nextChunkStart " << nextChunkStart << ", frame " << frame << ", count " << count << ", processed " << processed << std::endl;
-
-	if (!chunkSize) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	    std::cout << "Ending selection playback at " << nextChunkStart << std::endl;
-#endif
-	    // We need to maintain full buffers so that the other
-	    // thread can tell where it's got to in the playback -- so
-	    // return the full amount here
-	    frame = frame + count;
-	    return count;
-	}
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	std::cout << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << std::endl;
-#endif
-
-	size_t got = 0;
-
-	if (selectionSize < 100) {
-	    fadeIn = 0;
-	    fadeOut = 0;
-	} else if (selectionSize < 300) {
-	    if (fadeIn > 0) fadeIn = 10;
-	    if (fadeOut > 0) fadeOut = 10;
-	}
-
-	if (fadeIn > 0) {
-	    if (processed * 2 < fadeIn) {
-		fadeIn = processed * 2;
-	    }
-	}
-
-	if (fadeOut > 0) {
-	    if ((count - processed - chunkSize) * 2 < fadeOut) {
-		fadeOut = (count - processed - chunkSize) * 2;
-	    }
-	}
-
-	for (std::set<Model *>::iterator mi = m_models.begin();
-	     mi != m_models.end(); ++mi) {
-	    
-	    got = m_audioGenerator->mixModel(*mi, chunkStart, 
-					     chunkSize, chunkBufferPtrs,
-					     fadeIn, fadeOut);
-	}
-
-	for (size_t c = 0; c < channels; ++c) {
-	    chunkBufferPtrs[c] += chunkSize;
-	}
-
-	processed += chunkSize;
-	chunkStart = nextChunkStart;
-    }
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "Returning selection playback " << processed << " frames to " << nextChunkStart << std::endl;
-#endif
-
-    frame = nextChunkStart;
-    return processed;
-}
-
-void
-AudioCallbackPlaySource::unifyRingBuffers()
-{
-    if (m_readBuffers == m_writeBuffers) return;
-
-    // only unify if there will be something to read
-    for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-	RingBuffer<float> *wb = getWriteRingBuffer(c);
-	if (wb) {
-	    if (wb->getReadSpace() < m_blockSize * 2) {
-		if ((m_writeBufferFill + m_blockSize * 2) < 
-		    m_lastModelEndFrame) {
-		    // OK, we don't have enough and there's more to
-		    // read -- don't unify until we can do better
-		    return;
-		}
-	    }
-	    break;
-	}
-    }
-
-    size_t rf = m_readBufferFill;
-    RingBuffer<float> *rb = getReadRingBuffer(0);
-    if (rb) {
-	size_t rs = rb->getReadSpace();
-	//!!! incorrect when in non-contiguous selection, see comments elsewhere
-//	std::cout << "rs = " << rs << std::endl;
-	if (rs < rf) rf -= rs;
-	else rf = 0;
-    }
-    
-    //std::cout << "m_readBufferFill = " << m_readBufferFill << ", rf = " << rf << ", m_writeBufferFill = " << m_writeBufferFill << std::endl;
-
-    size_t wf = m_writeBufferFill;
-    size_t skip = 0;
-    for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-	RingBuffer<float> *wb = getWriteRingBuffer(c);
-	if (wb) {
-	    if (c == 0) {
-		
-		size_t wrs = wb->getReadSpace();
-//		std::cout << "wrs = " << wrs << std::endl;
-
-		if (wrs < wf) wf -= wrs;
-		else wf = 0;
-//		std::cout << "wf = " << wf << std::endl;
-		
-		if (wf < rf) skip = rf - wf;
-		if (skip == 0) break;
-	    }
-
-//	    std::cout << "skipping " << skip << std::endl;
-	    wb->skip(skip);
-	}
-    }
-		    
-    m_bufferScavenger.claim(m_readBuffers);
-    m_readBuffers = m_writeBuffers;
-    m_readBufferFill = m_writeBufferFill;
-//    std::cout << "unified" << std::endl;
-}
-
-void
-AudioCallbackPlaySource::FillThread::run()
-{
-    AudioCallbackPlaySource &s(m_source);
-    
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    std::cout << "AudioCallbackPlaySourceFillThread starting" << std::endl;
-#endif
-
-    s.m_mutex.lock();
-
-    bool previouslyPlaying = s.m_playing;
-    bool work = false;
-
-    while (!s.m_exiting) {
-
-	s.unifyRingBuffers();
-	s.m_bufferScavenger.scavenge();
-        s.m_pluginScavenger.scavenge();
-	s.m_timeStretcherScavenger.scavenge();
-
-	if (work && s.m_playing && s.getSourceSampleRate()) {
-	    
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	    std::cout << "AudioCallbackPlaySourceFillThread: not waiting" << std::endl;
-#endif
-
-	    s.m_mutex.unlock();
-	    s.m_mutex.lock();
-
-	} else {
-	    
-	    float ms = 100;
-	    if (s.getSourceSampleRate() > 0) {
-		ms = float(m_ringBufferSize) / float(s.getSourceSampleRate()) * 1000.0;
-	    }
-	    
-	    if (s.m_playing) ms /= 10;
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            if (!s.m_playing) std::cout << std::endl;
-	    std::cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << std::endl;
-#endif
-	    
-	    s.m_condition.wait(&s.m_mutex, size_t(ms));
-	}
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	std::cout << "AudioCallbackPlaySourceFillThread: awoken" << std::endl;
-#endif
-
-	work = false;
-
-	if (!s.getSourceSampleRate()) continue;
-
-	bool playing = s.m_playing;
-
-	if (playing && !previouslyPlaying) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	    std::cout << "AudioCallbackPlaySourceFillThread: playback state changed, resetting" << std::endl;
-#endif
-	    for (size_t c = 0; c < s.getTargetChannelCount(); ++c) {
-		RingBuffer<float> *rb = s.getReadRingBuffer(c);
-		if (rb) rb->reset();
-	    }
-	}
-	previouslyPlaying = playing;
-
-	work = s.fillBuffers();
-    }
-
-    s.m_mutex.unlock();
-}
-
--- a/audioio/AudioCallbackPlaySource.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,344 +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 _AUDIO_CALLBACK_PLAY_SOURCE_H_
-#define _AUDIO_CALLBACK_PLAY_SOURCE_H_
-
-#include "base/RingBuffer.h"
-#include "base/AudioPlaySource.h"
-#include "base/PropertyContainer.h"
-#include "base/Scavenger.h"
-
-#include <QObject>
-#include <QMutex>
-#include <QWaitCondition>
-
-#include "base/Thread.h"
-
-#include <samplerate.h>
-
-#include <set>
-#include <map>
-
-class Model;
-class ViewManager;
-class AudioGenerator;
-class PlayParameters;
-class PhaseVocoderTimeStretcher;
-class RealTimePluginInstance;
-
-/**
- * AudioCallbackPlaySource manages audio data supply to callback-based
- * audio APIs such as JACK or CoreAudio.  It maintains one ring buffer
- * per channel, filled during playback by a non-realtime thread, and
- * provides a method for a realtime thread to pick up the latest
- * available sample data from these buffers.
- */
-class AudioCallbackPlaySource : public virtual QObject,
-				public AudioPlaySource
-{
-    Q_OBJECT
-
-public:
-    AudioCallbackPlaySource(ViewManager *);
-    virtual ~AudioCallbackPlaySource();
-    
-    /**
-     * Add a data model to be played from.  The source can mix
-     * playback from a number of sources including dense and sparse
-     * models.  The models must match in sample rate, but they don't
-     * have to have identical numbers of channels.
-     */
-    virtual void addModel(Model *model);
-
-    /**
-     * Remove a model.
-     */
-    virtual void removeModel(Model *model);
-
-    /**
-     * Remove all models.  (Silence will ensue.)
-     */
-    virtual void clearModels();
-
-    /**
-     * Start making data available in the ring buffers for playback,
-     * from the given frame.  If playback is already under way, reseek
-     * to the given frame and continue.
-     */
-    virtual void play(size_t startFrame);
-
-    /**
-     * Stop playback and ensure that no more data is returned.
-     */
-    virtual void stop();
-
-    /**
-     * Return whether playback is currently supposed to be happening.
-     */
-    virtual bool isPlaying() const { return m_playing; }
-
-    /**
-     * Return the frame number that is currently expected to be coming
-     * out of the speakers.  (i.e. compensating for playback latency.)
-     */
-    virtual size_t getCurrentPlayingFrame();
-
-    /**
-     * Return the frame at which playback is expected to end (if not looping).
-     */
-    virtual size_t getPlayEndFrame() { return m_lastModelEndFrame; }
-
-    /**
-     * Set the block size of the target audio device.  This should
-     * be called by the target class.
-     */
-    void setTargetBlockSize(size_t);
-
-    /**
-     * Get the block size of the target audio device.
-     */
-    size_t getTargetBlockSize() const;
-
-    /**
-     * Set the playback latency of the target audio device, in frames
-     * at the target sample rate.  This is the difference between the
-     * frame currently "leaving the speakers" and the last frame (or
-     * highest last frame across all channels) requested via
-     * getSamples().  The default is zero.
-     */
-    void setTargetPlayLatency(size_t);
-
-    /**
-     * Get the playback latency of the target audio device.
-     */
-    size_t getTargetPlayLatency() const;
-
-    /**
-     * Specify that the target audio device has a fixed sample rate
-     * (i.e. cannot accommodate arbitrary sample rates based on the
-     * source).  If the target sets this to something other than the
-     * source sample rate, this class will resample automatically to
-     * fit.
-     */
-    void setTargetSampleRate(size_t);
-
-    /**
-     * Return the sample rate set by the target audio device (or the
-     * source sample rate if the target hasn't set one).
-     */
-    virtual size_t getTargetSampleRate() const;
-
-    /**
-     * Set the current output levels for metering (for call from the
-     * target)
-     */
-    void setOutputLevels(float left, float right);
-
-    /**
-     * Return the current (or thereabouts) output levels in the range
-     * 0.0 -> 1.0, for metering purposes.
-     */
-    virtual bool getOutputLevels(float &left, float &right);
-
-    /**
-     * Get the number of channels of audio that in the source models.
-     * This may safely be called from a realtime thread.  Returns 0 if
-     * there is no source yet available.
-     */
-    size_t getSourceChannelCount() const;
-
-    /**
-     * Get the number of channels of audio that will be provided
-     * to the play target.  This may be more than the source channel
-     * count: for example, a mono source will provide 2 channels
-     * after pan.
-     * This may safely be called from a realtime thread.  Returns 0 if
-     * there is no source yet available.
-     */
-    size_t getTargetChannelCount() const;
-
-    /**
-     * Get the actual sample rate of the source material.  This may
-     * safely be called from a realtime thread.  Returns 0 if there is
-     * no source yet available.
-     */
-    virtual size_t getSourceSampleRate() const;
-
-    /**
-     * Get "count" samples (at the target sample rate) of the mixed
-     * audio data, in all channels.  This may safely be called from a
-     * realtime thread.
-     */
-    size_t getSourceSamples(size_t count, float **buffer);
-
-    /**
-     * Set the time stretcher factor (i.e. playback speed).  Also
-     * specify whether the time stretcher will be variable rate
-     * (sharpening transients), and whether time stretching will be
-     * carried out on data mixed down to mono for speed.
-     */
-    void setTimeStretch(float factor, bool sharpen, bool mono);
-
-    /**
-     * Set the resampler quality, 0 - 2 where 0 is fastest and 2 is
-     * highest quality.
-     */
-    void setResampleQuality(int q);
-
-    /**
-     * Set a single real-time plugin as a processing effect for
-     * auditioning during playback.
-     *
-     * The plugin must have been initialised with
-     * getTargetChannelCount() channels and a getTargetBlockSize()
-     * sample frame processing block size.
-     *
-     * This playback source takes ownership of the plugin, which will
-     * be deleted at some point after the following call to
-     * setAuditioningPlugin (depending on real-time constraints).
-     *
-     * Pass a null pointer to remove the current auditioning plugin,
-     * if any.
-     */
-    void setAuditioningPlugin(RealTimePluginInstance *plugin);
-
-    /**
-     * Specify that only the given set of models should be played.
-     */
-    void setSoloModelSet(std::set<Model *>s);
-
-    /**
-     * Specify that all models should be played as normal (if not
-     * muted).
-     */
-    void clearSoloModelSet();
-
-signals:
-    void modelReplaced();
-
-    void playStatusChanged(bool isPlaying);
-
-    void sampleRateMismatch(size_t requested, size_t available, bool willResample);
-
-    void audioOverloadPluginDisabled();
-
-public slots:
-    void audioProcessingOverload();
-
-protected slots:
-    void selectionChanged();
-    void playLoopModeChanged();
-    void playSelectionModeChanged();
-    void playParametersChanged(PlayParameters *);
-    void preferenceChanged(PropertyContainer::PropertyName);
-    void modelChanged(size_t startFrame, size_t endFrame);
-
-protected:
-    ViewManager                     *m_viewManager;
-    AudioGenerator                  *m_audioGenerator;
-
-    class RingBufferVector : public std::vector<RingBuffer<float> *> {
-    public:
-	virtual ~RingBufferVector() {
-	    while (!empty()) {
-		delete *begin();
-		erase(begin());
-	    }
-	}
-    };
-
-    std::set<Model *>                 m_models;
-    RingBufferVector                 *m_readBuffers;
-    RingBufferVector                 *m_writeBuffers;
-    size_t                            m_readBufferFill;
-    size_t                            m_writeBufferFill;
-    Scavenger<RingBufferVector>       m_bufferScavenger;
-    size_t                            m_sourceChannelCount;
-    size_t                            m_blockSize;
-    size_t                            m_sourceSampleRate;
-    size_t                            m_targetSampleRate;
-    size_t                            m_playLatency;
-    bool                              m_playing;
-    bool                              m_exiting;
-    size_t                            m_lastModelEndFrame;
-    static const size_t               m_ringBufferSize;
-    float                             m_outputLeft;
-    float                             m_outputRight;
-    RealTimePluginInstance           *m_auditioningPlugin;
-    bool                              m_auditioningPluginBypassed;
-    Scavenger<RealTimePluginInstance> m_pluginScavenger;
-
-    RingBuffer<float> *getWriteRingBuffer(size_t c) {
-	if (m_writeBuffers && c < m_writeBuffers->size()) {
-	    return (*m_writeBuffers)[c];
-	} else {
-	    return 0;
-	}
-    }
-
-    RingBuffer<float> *getReadRingBuffer(size_t c) {
-	RingBufferVector *rb = m_readBuffers;
-	if (rb && c < rb->size()) {
-	    return (*rb)[c];
-	} else {
-	    return 0;
-	}
-    }
-
-    void clearRingBuffers(bool haveLock = false, size_t count = 0);
-    void unifyRingBuffers();
-
-    PhaseVocoderTimeStretcher *m_timeStretcher;
-    Scavenger<PhaseVocoderTimeStretcher> m_timeStretcherScavenger;
-
-    // Called from fill thread, m_playing true, mutex held
-    // Return true if work done
-    bool fillBuffers();
-    
-    // Called from fillBuffers.  Return the number of frames written,
-    // which will be count or fewer.  Return in the frame argument the
-    // new buffered frame position (which may be earlier than the
-    // frame argument passed in, in the case of looping).
-    size_t mixModels(size_t &frame, size_t count, float **buffers);
-
-    // Called from getSourceSamples.
-    void applyAuditioningEffect(size_t count, float **buffers);
-
-    class FillThread : public Thread
-    {
-    public:
-	FillThread(AudioCallbackPlaySource &source) :
-            Thread(Thread::NonRTThread),
-	    m_source(source) { }
-
-	virtual void run();
-
-    protected:
-	AudioCallbackPlaySource &m_source;
-    };
-
-    QMutex m_mutex;
-    QWaitCondition m_condition;
-    FillThread *m_fillThread;
-    SRC_STATE *m_converter;
-    SRC_STATE *m_crapConverter; // for use when playing very fast
-    int m_resampleQuality;
-    void initialiseConverter();
-};
-
-#endif
-
-
--- a/audioio/AudioCallbackPlayTarget.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /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 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 "AudioCallbackPlayTarget.h"
-#include "AudioCallbackPlaySource.h"
-
-#include <iostream>
-
-AudioCallbackPlayTarget::AudioCallbackPlayTarget(AudioCallbackPlaySource *source) :
-    m_source(source),
-    m_outputGain(1.0)
-{
-    if (m_source) {
-	connect(m_source, SIGNAL(modelReplaced()),
-		this, SLOT(sourceModelReplaced()));
-    }
-}
-
-AudioCallbackPlayTarget::~AudioCallbackPlayTarget()
-{
-}
-
-void
-AudioCallbackPlayTarget::setOutputGain(float gain)
-{
-    m_outputGain = gain;
-}
-
--- a/audioio/AudioCallbackPlayTarget.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +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 _AUDIO_CALLBACK_PLAY_TARGET_H_
-#define _AUDIO_CALLBACK_PLAY_TARGET_H_
-
-#include <QObject>
-
-class AudioCallbackPlaySource;
-
-class AudioCallbackPlayTarget : public QObject
-{
-    Q_OBJECT
-
-public:
-    AudioCallbackPlayTarget(AudioCallbackPlaySource *source);
-    virtual ~AudioCallbackPlayTarget();
-
-    virtual bool isOK() const = 0;
-
-    float getOutputGain() const {
-	return m_outputGain;
-    }
-
-public slots:
-    /**
-     * Set the playback gain (0.0 = silence, 1.0 = levels unmodified)
-     */
-    virtual void setOutputGain(float gain);
-
-    /**
-     * The main source model (providing the playback sample rate) has
-     * been changed.  The target should query the source's sample
-     * rate, set its output sample rate accordingly, and call back on
-     * the source's setTargetSampleRate to indicate what sample rate
-     * it succeeded in setting at the output.  If this differs from
-     * the model rate, the source will resample.
-     */
-    virtual void sourceModelReplaced() = 0;
-
-protected:
-    AudioCallbackPlaySource *m_source;
-    float m_outputGain;
-};
-
-#endif
-
--- a/audioio/AudioCoreAudioTarget.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,22 +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.
-*/
-
-#ifdef HAVE_COREAUDIO
-
-#include "AudioCoreAudioTarget.h"
-
-
-
-#endif
--- a/audioio/AudioCoreAudioTarget.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +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 _AUDIO_CORE_AUDIO_TARGET_H_
-#define _AUDIO_CORE_AUDIO_TARGET_H_
-
-#ifdef HAVE_COREAUDIO
-
-#include <jack/jack.h>
-#include <vector>
-
-#include <CoreAudio/CoreAudio.h>
-#include <CoreAudio/CoreAudioTypes.h>
-#include <AudioUnit/AUComponent.h>
-#include <AudioUnit/AudioUnitProperties.h>
-#include <AudioUnit/AudioUnitParameters.h>
-#include <AudioUnit/AudioOutputUnit.h>
-
-#include "AudioCallbackPlayTarget.h"
-
-class AudioCallbackPlaySource;
-
-class AudioCoreAudioTarget : public AudioCallbackPlayTarget
-{
-    Q_OBJECT
-
-public:
-    AudioCoreAudioTarget(AudioCallbackPlaySource *source);
-    ~AudioCoreAudioTarget();
-
-    virtual bool isOK() const;
-
-public slots:
-    virtual void sourceModelReplaced();
-
-protected:
-    OSStatus process(void *data,
-		     AudioUnitRenderActionFlags *flags,
-		     const AudioTimeStamp *timestamp,
-		     unsigned int inbus,
-		     unsigned int inframes,
-		     AudioBufferList *ioData);
-
-    int m_bufferSize;
-    int m_sampleRate;
-    int m_latency;
-};
-
-#endif /* HAVE_COREAUDIO */
-
-#endif
-
--- a/audioio/AudioGenerator.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,799 +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 "AudioGenerator.h"
-
-#include "base/TempDirectory.h"
-#include "base/PlayParameters.h"
-#include "base/PlayParameterRepository.h"
-#include "base/Pitch.h"
-#include "base/Exceptions.h"
-
-#include "data/model/NoteModel.h"
-#include "data/model/DenseTimeValueModel.h"
-#include "data/model/SparseOneDimensionalModel.h"
-
-#include "plugin/RealTimePluginFactory.h"
-#include "plugin/RealTimePluginInstance.h"
-#include "plugin/PluginIdentifier.h"
-#include "plugin/PluginXml.h"
-#include "plugin/api/alsa/seq_event.h"
-
-#include <iostream>
-#include <math.h>
-
-#include <QDir>
-#include <QFile>
-
-const size_t
-AudioGenerator::m_pluginBlockSize = 2048;
-
-QString
-AudioGenerator::m_sampleDir = "";
-
-//#define DEBUG_AUDIO_GENERATOR 1
-
-AudioGenerator::AudioGenerator() :
-    m_sourceSampleRate(0),
-    m_targetChannelCount(1),
-    m_soloing(false)
-{
-    connect(PlayParameterRepository::getInstance(),
-            SIGNAL(playPluginIdChanged(const Model *, QString)),
-            this,
-            SLOT(playPluginIdChanged(const Model *, QString)));
-
-    connect(PlayParameterRepository::getInstance(),
-            SIGNAL(playPluginConfigurationChanged(const Model *, QString)),
-            this,
-            SLOT(playPluginConfigurationChanged(const Model *, QString)));
-}
-
-AudioGenerator::~AudioGenerator()
-{
-}
-
-bool
-AudioGenerator::canPlay(const Model *model)
-{
-    if (dynamic_cast<const DenseTimeValueModel *>(model) ||
-	dynamic_cast<const SparseOneDimensionalModel *>(model) ||
-	dynamic_cast<const NoteModel *>(model)) {
-	return true;
-    } else {
-	return false;
-    }
-}
-
-bool
-AudioGenerator::addModel(Model *model)
-{
-    if (m_sourceSampleRate == 0) {
-
-	m_sourceSampleRate = model->getSampleRate();
-
-    } else {
-
-	DenseTimeValueModel *dtvm =
-	    dynamic_cast<DenseTimeValueModel *>(model);
-
-	if (dtvm) {
-	    m_sourceSampleRate = model->getSampleRate();
-	    return true;
-	}
-    }
-
-    RealTimePluginInstance *plugin = loadPluginFor(model);
-    if (plugin) {
-        QMutexLocker locker(&m_mutex);
-        m_synthMap[model] = plugin;
-        return true;
-    }
-
-    return false;
-}
-
-void
-AudioGenerator::playPluginIdChanged(const Model *model, QString)
-{
-    if (m_synthMap.find(model) == m_synthMap.end()) return;
-    
-    RealTimePluginInstance *plugin = loadPluginFor(model);
-    if (plugin) {
-        QMutexLocker locker(&m_mutex);
-        delete m_synthMap[model];
-        m_synthMap[model] = plugin;
-    }
-}
-
-void
-AudioGenerator::playPluginConfigurationChanged(const Model *model,
-                                               QString configurationXml)
-{
-//    std::cerr << "AudioGenerator::playPluginConfigurationChanged" << std::endl;
-
-    if (m_synthMap.find(model) == m_synthMap.end()) {
-        std::cerr << "AudioGenerator::playPluginConfigurationChanged: We don't know about this plugin" << std::endl;
-        return;
-    }
-
-    RealTimePluginInstance *plugin = m_synthMap[model];
-    if (plugin) {
-        PluginXml(plugin).setParametersFromXml(configurationXml);
-    }
-}
-
-QString
-AudioGenerator::getDefaultPlayPluginId(const Model *model)
-{
-    const SparseOneDimensionalModel *sodm =
-        dynamic_cast<const SparseOneDimensionalModel *>(model);
-    if (sodm) {
-        return QString("dssi:%1:sample_player").
-            arg(PluginIdentifier::BUILTIN_PLUGIN_SONAME);
-    }
-
-    const NoteModel *nm = dynamic_cast<const NoteModel *>(model);
-    if (nm) {
-        return QString("dssi:%1:sample_player").
-            arg(PluginIdentifier::BUILTIN_PLUGIN_SONAME);
-    }  
-    
-    return "";
-}
-
-QString
-AudioGenerator::getDefaultPlayPluginConfiguration(const Model *model)
-{
-    QString program = "";
-
-    const SparseOneDimensionalModel *sodm =
-        dynamic_cast<const SparseOneDimensionalModel *>(model);
-    if (sodm) {
-        program = "tap";
-    }
-
-    const NoteModel *nm = dynamic_cast<const NoteModel *>(model);
-    if (nm) {
-        program = "piano";
-    }
-
-    if (program == "") return "";
-
-    return
-        QString("<plugin configuration=\"%1\" program=\"%2\"/>")
-        .arg(XmlExportable::encodeEntities
-             (QString("sampledir=%1")
-              .arg(PluginXml::encodeConfigurationChars(getSampleDir()))))
-        .arg(XmlExportable::encodeEntities(program));
-}    
-
-QString
-AudioGenerator::getSampleDir()
-{
-    if (m_sampleDir != "") return m_sampleDir;
-
-    try {
-        m_sampleDir = TempDirectory::getInstance()->getSubDirectoryPath("samples");
-    } catch (DirectoryCreationFailed f) {
-        std::cerr << "WARNING: AudioGenerator::getSampleDir: Failed to create "
-                  << "temporary sample directory" << std::endl;
-        m_sampleDir = "";
-        return "";
-    }
-
-    QDir sampleResourceDir(":/samples", "*.wav");
-
-    for (unsigned int i = 0; i < sampleResourceDir.count(); ++i) {
-
-        QString fileName(sampleResourceDir[i]);
-        QFile file(sampleResourceDir.filePath(fileName));
-
-        if (!file.copy(QDir(m_sampleDir).filePath(fileName))) {
-            std::cerr << "WARNING: AudioGenerator::getSampleDir: "
-                      << "Unable to copy " << fileName.toStdString()
-                      << " into temporary directory \""
-                      << m_sampleDir.toStdString() << "\"" << std::endl;
-        }
-    }
-
-    return m_sampleDir;
-}
-
-void
-AudioGenerator::setSampleDir(RealTimePluginInstance *plugin)
-{
-    plugin->configure("sampledir", getSampleDir().toStdString());
-} 
-
-RealTimePluginInstance *
-AudioGenerator::loadPluginFor(const Model *model)
-{
-    QString pluginId, configurationXml;
-
-    PlayParameters *parameters =
-	PlayParameterRepository::getInstance()->getPlayParameters(model);
-    if (parameters) {
-        pluginId = parameters->getPlayPluginId();
-        configurationXml = parameters->getPlayPluginConfiguration();
-    }
-
-    if (pluginId == "") {
-        pluginId = getDefaultPlayPluginId(model);
-        configurationXml = getDefaultPlayPluginConfiguration(model);
-    }
-
-    if (pluginId == "") return 0;
-
-    RealTimePluginInstance *plugin = loadPlugin(pluginId, "");
-    if (!plugin) return 0;
-
-    if (configurationXml != "") {
-        PluginXml(plugin).setParametersFromXml(configurationXml);
-    }
-
-    if (parameters) {
-        parameters->setPlayPluginId(pluginId);
-        parameters->setPlayPluginConfiguration(configurationXml);
-    }
-
-    return plugin;
-}
-
-RealTimePluginInstance *
-AudioGenerator::loadPlugin(QString pluginId, QString program)
-{
-    RealTimePluginFactory *factory =
-	RealTimePluginFactory::instanceFor(pluginId);
-    
-    if (!factory) {
-	std::cerr << "Failed to get plugin factory" << std::endl;
-	return false;
-    }
-	
-    RealTimePluginInstance *instance =
-	factory->instantiatePlugin
-	(pluginId, 0, 0, m_sourceSampleRate, m_pluginBlockSize, m_targetChannelCount);
-
-    if (!instance) {
-	std::cerr << "Failed to instantiate plugin " << pluginId.toStdString() << std::endl;
-        return 0;
-    }
-
-    setSampleDir(instance);
-
-    for (unsigned int i = 0; i < instance->getParameterCount(); ++i) {
-        instance->setParameterValue(i, instance->getParameterDefault(i));
-    }
-    std::string defaultProgram = instance->getProgram(0, 0);
-    if (defaultProgram != "") {
-//        std::cerr << "first selecting default program " << defaultProgram << std::endl;
-        instance->selectProgram(defaultProgram);
-    }
-    if (program != "") {
-//        std::cerr << "now selecting desired program " << program.toStdString() << std::endl;
-        instance->selectProgram(program.toStdString());
-    }
-    instance->setIdealChannelCount(m_targetChannelCount); // reset!
-
-    return instance;
-}
-
-void
-AudioGenerator::removeModel(Model *model)
-{
-    SparseOneDimensionalModel *sodm =
-	dynamic_cast<SparseOneDimensionalModel *>(model);
-    if (!sodm) return; // nothing to do
-
-    QMutexLocker locker(&m_mutex);
-
-    if (m_synthMap.find(sodm) == m_synthMap.end()) return;
-
-    RealTimePluginInstance *instance = m_synthMap[sodm];
-    m_synthMap.erase(sodm);
-    delete instance;
-}
-
-void
-AudioGenerator::clearModels()
-{
-    QMutexLocker locker(&m_mutex);
-    while (!m_synthMap.empty()) {
-	RealTimePluginInstance *instance = m_synthMap.begin()->second;
-	m_synthMap.erase(m_synthMap.begin());
-	delete instance;
-    }
-}    
-
-void
-AudioGenerator::reset()
-{
-    QMutexLocker locker(&m_mutex);
-    for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
-	if (i->second) {
-	    i->second->silence();
-	    i->second->discardEvents();
-	}
-    }
-
-    m_noteOffs.clear();
-}
-
-void
-AudioGenerator::setTargetChannelCount(size_t targetChannelCount)
-{
-    if (m_targetChannelCount == targetChannelCount) return;
-
-//    std::cerr << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << std::endl;
-
-    QMutexLocker locker(&m_mutex);
-    m_targetChannelCount = targetChannelCount;
-
-    for (PluginMap::iterator i = m_synthMap.begin(); i != m_synthMap.end(); ++i) {
-	if (i->second) i->second->setIdealChannelCount(targetChannelCount);
-    }
-}
-
-size_t
-AudioGenerator::getBlockSize() const
-{
-    return m_pluginBlockSize;
-}
-
-void
-AudioGenerator::setSoloModelSet(std::set<Model *> s)
-{
-    QMutexLocker locker(&m_mutex);
-
-    m_soloModelSet = s;
-    m_soloing = true;
-}
-
-void
-AudioGenerator::clearSoloModelSet()
-{
-    QMutexLocker locker(&m_mutex);
-
-    m_soloModelSet.clear();
-    m_soloing = false;
-}
-
-size_t
-AudioGenerator::mixModel(Model *model, size_t startFrame, size_t frameCount,
-			 float **buffer, size_t fadeIn, size_t fadeOut)
-{
-    if (m_sourceSampleRate == 0) {
-	std::cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << std::endl;
-	return frameCount;
-    }
-
-    QMutexLocker locker(&m_mutex);
-
-    PlayParameters *parameters =
-	PlayParameterRepository::getInstance()->getPlayParameters(model);
-    if (!parameters) return frameCount;
-
-    bool playing = !parameters->isPlayMuted();
-    if (!playing) {
-#ifdef DEBUG_AUDIO_GENERATOR
-        std::cout << "AudioGenerator::mixModel(" << model << "): muted" << std::endl;
-#endif
-        return frameCount;
-    }
-
-    if (m_soloing) {
-        if (m_soloModelSet.find(model) == m_soloModelSet.end()) {
-#ifdef DEBUG_AUDIO_GENERATOR
-            std::cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << std::endl;
-#endif
-            return frameCount;
-        }
-    }
-
-    float gain = parameters->getPlayGain();
-    float pan = parameters->getPlayPan();
-
-    DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
-    if (dtvm) {
-	return mixDenseTimeValueModel(dtvm, startFrame, frameCount,
-				      buffer, gain, pan, fadeIn, fadeOut);
-    }
-
-    SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
-	(model);
-    if (sodm) {
-	return mixSparseOneDimensionalModel(sodm, startFrame, frameCount,
-					    buffer, gain, pan, fadeIn, fadeOut);
-    }
-
-    NoteModel *nm = dynamic_cast<NoteModel *>(model);
-    if (nm) {
-	return mixNoteModel(nm, startFrame, frameCount,
-			    buffer, gain, pan, fadeIn, fadeOut);
-    }
-
-    return frameCount;
-}
-
-size_t
-AudioGenerator::mixDenseTimeValueModel(DenseTimeValueModel *dtvm,
-				       size_t startFrame, size_t frames,
-				       float **buffer, float gain, float pan,
-				       size_t fadeIn, size_t fadeOut)
-{
-    static float *channelBuffer = 0;
-    static size_t channelBufSiz = 0;
-
-    size_t totalFrames = frames + fadeIn/2 + fadeOut/2;
-
-    if (channelBufSiz < totalFrames) {
-	delete[] channelBuffer;
-	channelBuffer = new float[totalFrames];
-	channelBufSiz = totalFrames;
-    }
-    
-    size_t got = 0;
-    size_t prevChannel = 999;
-
-    for (size_t c = 0; c < m_targetChannelCount; ++c) {
-
-	size_t sourceChannel = (c % dtvm->getChannelCount());
-
-//	std::cerr << "mixing channel " << c << " from source channel " << sourceChannel << std::endl;
-
-	float channelGain = gain;
-	if (pan != 0.0) {
-	    if (c == 0) {
-		if (pan > 0.0) channelGain *= 1.0 - pan;
-	    } else {
-		if (pan < 0.0) channelGain *= pan + 1.0;
-	    }
-	}
-
-	if (prevChannel != sourceChannel) {
-	    if (startFrame >= fadeIn/2) {
-		got = dtvm->getData
-		    (sourceChannel,
-		     startFrame - fadeIn/2,
-                     frames + fadeOut/2 + fadeIn/2,
-		     channelBuffer);
-	    } else {
-		size_t missing = fadeIn/2 - startFrame;
-		got = dtvm->getData
-		    (sourceChannel,
-		     startFrame,
-                     frames + fadeOut/2,
-		     channelBuffer + missing);
-	    }	    
-	}
-	prevChannel = sourceChannel;
-
-	for (size_t i = 0; i < fadeIn/2; ++i) {
-	    float *back = buffer[c];
-	    back -= fadeIn/2;
-	    back[i] += (channelGain * channelBuffer[i] * i) / fadeIn;
-	}
-
-	for (size_t i = 0; i < frames + fadeOut/2; ++i) {
-	    float mult = channelGain;
-	    if (i < fadeIn/2) {
-		mult = (mult * i) / fadeIn;
-	    }
-	    if (i > frames - fadeOut/2) {
-		mult = (mult * ((frames + fadeOut/2) - i)) / fadeOut;
-	    }
-	    buffer[c][i] += mult * channelBuffer[i];
-	}
-    }
-
-    return got;
-}
-  
-size_t
-AudioGenerator::mixSparseOneDimensionalModel(SparseOneDimensionalModel *sodm,
-					     size_t startFrame, size_t frames,
-					     float **buffer, float gain, float pan,
-					     size_t /* fadeIn */,
-					     size_t /* fadeOut */)
-{
-    RealTimePluginInstance *plugin = m_synthMap[sodm];
-    if (!plugin) return 0;
-
-    size_t latency = plugin->getLatency();
-    size_t blocks = frames / m_pluginBlockSize;
-    
-    //!!! hang on -- the fact that the audio callback play source's
-    //buffer is a multiple of the plugin's buffer size doesn't mean
-    //that we always get called for a multiple of it here (because it
-    //also depends on the JACK block size).  how should we ensure that
-    //all models write the same amount in to the mix, and that we
-    //always have a multiple of the plugin buffer size?  I guess this
-    //class has to be queryable for the plugin buffer size & the
-    //callback play source has to use that as a multiple for all the
-    //calls to mixModel
-
-    size_t got = blocks * m_pluginBlockSize;
-
-#ifdef DEBUG_AUDIO_GENERATOR
-    std::cout << "mixModel [sparse]: frames " << frames
-	      << ", blocks " << blocks << std::endl;
-#endif
-
-    snd_seq_event_t onEv;
-    onEv.type = SND_SEQ_EVENT_NOTEON;
-    onEv.data.note.channel = 0;
-    onEv.data.note.note = 64;
-    onEv.data.note.velocity = 100;
-
-    snd_seq_event_t offEv;
-    offEv.type = SND_SEQ_EVENT_NOTEOFF;
-    offEv.data.note.channel = 0;
-    offEv.data.note.velocity = 0;
-    
-    NoteOffSet &noteOffs = m_noteOffs[sodm];
-
-    for (size_t i = 0; i < blocks; ++i) {
-
-	size_t reqStart = startFrame + i * m_pluginBlockSize;
-
-	SparseOneDimensionalModel::PointList points =
-	    sodm->getPoints(reqStart + latency,
-			    reqStart + latency + m_pluginBlockSize);
-
-        Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
-	    (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
-
-	for (SparseOneDimensionalModel::PointList::iterator pli =
-		 points.begin(); pli != points.end(); ++pli) {
-
-	    size_t pliFrame = pli->frame;
-
-	    if (pliFrame >= latency) pliFrame -= latency;
-
-	    if (pliFrame < reqStart ||
-		pliFrame >= reqStart + m_pluginBlockSize) continue;
-
-	    while (noteOffs.begin() != noteOffs.end() &&
-		   noteOffs.begin()->frame <= pliFrame) {
-
-                Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		    (noteOffs.begin()->frame, m_sourceSampleRate);
-
-		offEv.data.note.note = noteOffs.begin()->pitch;
-
-#ifdef DEBUG_AUDIO_GENERATOR
-		std::cerr << "mixModel [sparse]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << std::endl;
-#endif
-
-		plugin->sendEvent(eventTime, &offEv);
-		noteOffs.erase(noteOffs.begin());
-	    }
-
-            Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		(pliFrame, m_sourceSampleRate);
-	    
-	    plugin->sendEvent(eventTime, &onEv);
-
-#ifdef DEBUG_AUDIO_GENERATOR
-	    std::cout << "mixModel [sparse]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl;
-#endif
-	    
-	    size_t duration = 7000; // frames [for now]
-	    NoteOff noff;
-	    noff.pitch = onEv.data.note.note;
-	    noff.frame = pliFrame + duration;
-	    noteOffs.insert(noff);
-	}
-
-	while (noteOffs.begin() != noteOffs.end() &&
-	       noteOffs.begin()->frame <=
-	       startFrame + i * m_pluginBlockSize + m_pluginBlockSize) {
-
-            Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		(noteOffs.begin()->frame, m_sourceSampleRate);
-
-	    offEv.data.note.note = noteOffs.begin()->pitch;
-
-#ifdef DEBUG_AUDIO_GENERATOR
-		std::cerr << "mixModel [sparse]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << std::endl;
-#endif
-
-	    plugin->sendEvent(eventTime, &offEv);
-	    noteOffs.erase(noteOffs.begin());
-	}
-	
-	plugin->run(blockTime);
-	float **outs = plugin->getAudioOutputBuffers();
-
-	for (size_t c = 0; c < m_targetChannelCount; ++c) {
-#ifdef DEBUG_AUDIO_GENERATOR
-	    std::cout << "mixModel [sparse]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl;
-#endif
-
-	    size_t sourceChannel = (c % plugin->getAudioOutputCount());
-
-	    float channelGain = gain;
-	    if (pan != 0.0) {
-		if (c == 0) {
-		    if (pan > 0.0) channelGain *= 1.0 - pan;
-		} else {
-		    if (pan < 0.0) channelGain *= pan + 1.0;
-		}
-	    }
-
-	    for (size_t j = 0; j < m_pluginBlockSize; ++j) {
-		buffer[c][i * m_pluginBlockSize + j] +=
-		    channelGain * outs[sourceChannel][j];
-	    }
-	}
-    }
-
-    return got;
-}
-
-    
-//!!! mucho duplication with above -- refactor
-size_t
-AudioGenerator::mixNoteModel(NoteModel *nm,
-			     size_t startFrame, size_t frames,
-			     float **buffer, float gain, float pan,
-			     size_t /* fadeIn */,
-			     size_t /* fadeOut */)
-{
-    RealTimePluginInstance *plugin = m_synthMap[nm];
-    if (!plugin) return 0;
-
-    size_t latency = plugin->getLatency();
-    size_t blocks = frames / m_pluginBlockSize;
-    
-    //!!! hang on -- the fact that the audio callback play source's
-    //buffer is a multiple of the plugin's buffer size doesn't mean
-    //that we always get called for a multiple of it here (because it
-    //also depends on the JACK block size).  how should we ensure that
-    //all models write the same amount in to the mix, and that we
-    //always have a multiple of the plugin buffer size?  I guess this
-    //class has to be queryable for the plugin buffer size & the
-    //callback play source has to use that as a multiple for all the
-    //calls to mixModel
-
-    size_t got = blocks * m_pluginBlockSize;
-
-#ifdef DEBUG_AUDIO_GENERATOR
-    std::cout << "mixModel [note]: frames " << frames
-	      << ", blocks " << blocks << std::endl;
-#endif
-
-    snd_seq_event_t onEv;
-    onEv.type = SND_SEQ_EVENT_NOTEON;
-    onEv.data.note.channel = 0;
-    onEv.data.note.note = 64;
-    onEv.data.note.velocity = 100;
-
-    snd_seq_event_t offEv;
-    offEv.type = SND_SEQ_EVENT_NOTEOFF;
-    offEv.data.note.channel = 0;
-    offEv.data.note.velocity = 0;
-    
-    NoteOffSet &noteOffs = m_noteOffs[nm];
-
-    for (size_t i = 0; i < blocks; ++i) {
-
-	size_t reqStart = startFrame + i * m_pluginBlockSize;
-
-	NoteModel::PointList points =
-	    nm->getPoints(reqStart + latency,
-			    reqStart + latency + m_pluginBlockSize);
-
-        Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
-	    (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
-
-	for (NoteModel::PointList::iterator pli =
-		 points.begin(); pli != points.end(); ++pli) {
-
-	    size_t pliFrame = pli->frame;
-
-	    if (pliFrame >= latency) pliFrame -= latency;
-
-	    if (pliFrame < reqStart ||
-		pliFrame >= reqStart + m_pluginBlockSize) continue;
-
-	    while (noteOffs.begin() != noteOffs.end() &&
-		   noteOffs.begin()->frame <= pliFrame) {
-
-                Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		    (noteOffs.begin()->frame, m_sourceSampleRate);
-
-		offEv.data.note.note = noteOffs.begin()->pitch;
-
-#ifdef DEBUG_AUDIO_GENERATOR
-		std::cerr << "mixModel [note]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << std::endl;
-#endif
-
-		plugin->sendEvent(eventTime, &offEv);
-		noteOffs.erase(noteOffs.begin());
-	    }
-
-            Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		(pliFrame, m_sourceSampleRate);
-	    
-            if (nm->getScaleUnits() == "Hz") {
-                onEv.data.note.note = Pitch::getPitchForFrequency(pli->value);
-            } else {
-                onEv.data.note.note = lrintf(pli->value);
-            }
-
-	    plugin->sendEvent(eventTime, &onEv);
-
-#ifdef DEBUG_AUDIO_GENERATOR
-	    std::cout << "mixModel [note]: point at frame " << pliFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << std::endl;
-#endif
-	    
-	    size_t duration = pli->duration;
-            if (duration == 0 || duration == 1) {
-                duration = m_sourceSampleRate / 20;
-            }
-	    NoteOff noff;
-	    noff.pitch = onEv.data.note.note;
-	    noff.frame = pliFrame + duration;
-	    noteOffs.insert(noff);
-	}
-
-	while (noteOffs.begin() != noteOffs.end() &&
-	       noteOffs.begin()->frame <=
-	       startFrame + i * m_pluginBlockSize + m_pluginBlockSize) {
-
-            Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		(noteOffs.begin()->frame, m_sourceSampleRate);
-
-	    offEv.data.note.note = noteOffs.begin()->pitch;
-
-#ifdef DEBUG_AUDIO_GENERATOR
-		std::cerr << "mixModel [note]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << std::endl;
-#endif
-
-	    plugin->sendEvent(eventTime, &offEv);
-	    noteOffs.erase(noteOffs.begin());
-	}
-	
-	plugin->run(blockTime);
-	float **outs = plugin->getAudioOutputBuffers();
-
-	for (size_t c = 0; c < m_targetChannelCount; ++c) {
-#ifdef DEBUG_AUDIO_GENERATOR
-	    std::cout << "mixModel [note]: adding " << m_pluginBlockSize << " samples from plugin output " << c << std::endl;
-#endif
-
-	    size_t sourceChannel = (c % plugin->getAudioOutputCount());
-
-	    float channelGain = gain;
-	    if (pan != 0.0) {
-		if (c == 0) {
-		    if (pan > 0.0) channelGain *= 1.0 - pan;
-		} else {
-		    if (pan < 0.0) channelGain *= pan + 1.0;
-		}
-	    }
-
-	    for (size_t j = 0; j < m_pluginBlockSize; ++j) {
-		buffer[c][i * m_pluginBlockSize + j] += 
-		    channelGain * outs[sourceChannel][j];
-	    }
-	}
-    }
-
-    return got;
-}
-
--- a/audioio/AudioGenerator.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,158 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-#ifndef _AUDIO_GENERATOR_H_
-#define _AUDIO_GENERATOR_H_
-
-class Model;
-class NoteModel;
-class DenseTimeValueModel;
-class SparseOneDimensionalModel;
-class RealTimePluginInstance;
-
-#include <QObject>
-#include <QMutex>
-
-#include <set>
-#include <map>
-
-class AudioGenerator : public QObject
-{
-    Q_OBJECT
-
-public:
-    AudioGenerator();
-    virtual ~AudioGenerator();
-
-    /**
-     * Return true if the given model is of a type that we generally
-     * know how to play.  This doesn't guarantee that a specific
-     * AudioGenerator will actually produce sounds for it (for
-     * example, it may turn out that a vital plugin is missing).
-     */
-    static bool canPlay(const Model *model);
-
-    static QString getDefaultPlayPluginId(const Model *model);
-    static QString getDefaultPlayPluginConfiguration(const Model *model);
-
-    /**
-     * Add a data model to be played from and initialise any necessary
-     * audio generation code.  Returns true if the model will be
-     * played.  (The return value test here is stricter than that for
-     * canPlay, above.)  The model will be added regardless of the
-     * return value.
-     */
-    virtual bool addModel(Model *model);
-
-    /**
-     * Remove a model.
-     */
-    virtual void removeModel(Model *model);
-
-    /**
-     * Remove all models.
-     */
-    virtual void clearModels();
-
-    /**
-     * Reset playback, clearing plugins and the like.
-     */
-    virtual void reset();
-
-    /**
-     * Set the target channel count.  The buffer parameter to mixModel
-     * must always point to at least this number of arrays.
-     */
-    virtual void setTargetChannelCount(size_t channelCount);
-
-    /**
-     * Return the internal processing block size.  The frameCount
-     * argument to all mixModel calls must be a multiple of this
-     * value.
-     */
-    virtual size_t getBlockSize() const;
-
-    /**
-     * Mix a single model into an output buffer.
-     */
-    virtual size_t mixModel(Model *model, size_t startFrame, size_t frameCount,
-			    float **buffer, size_t fadeIn = 0, size_t fadeOut = 0);
-
-    /**
-     * Specify that only the given set of models should be played.
-     */
-    virtual void setSoloModelSet(std::set<Model *>s);
-
-    /**
-     * Specify that all models should be played as normal (if not
-     * muted).
-     */
-    virtual void clearSoloModelSet();
-
-protected slots:
-    void playPluginIdChanged(const Model *, QString);
-    void playPluginConfigurationChanged(const Model *, QString);
-
-protected:
-    size_t       m_sourceSampleRate;
-    size_t       m_targetChannelCount;
-
-    bool m_soloing;
-    std::set<Model *> m_soloModelSet;
-
-    struct NoteOff {
-
-	int pitch;
-	size_t frame;
-
-	struct Comparator {
-	    bool operator()(const NoteOff &n1, const NoteOff &n2) const {
-		return n1.frame < n2.frame;
-	    }
-	};
-    };
-
-    typedef std::map<const Model *, RealTimePluginInstance *> PluginMap;
-
-    typedef std::set<NoteOff, NoteOff::Comparator> NoteOffSet;
-    typedef std::map<const Model *, NoteOffSet> NoteOffMap;
-
-    QMutex m_mutex;
-    PluginMap m_synthMap;
-    NoteOffMap m_noteOffs;
-    static QString m_sampleDir;
-
-    virtual RealTimePluginInstance *loadPluginFor(const Model *model);
-    virtual RealTimePluginInstance *loadPlugin(QString id, QString program);
-    static QString getSampleDir();
-    static void setSampleDir(RealTimePluginInstance *plugin);
-
-    virtual size_t mixDenseTimeValueModel
-    (DenseTimeValueModel *model, size_t startFrame, size_t frameCount,
-     float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut);
-
-    virtual size_t mixSparseOneDimensionalModel
-    (SparseOneDimensionalModel *model, size_t startFrame, size_t frameCount,
-     float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut);
-
-    virtual size_t mixNoteModel
-    (NoteModel *model, size_t startFrame, size_t frameCount,
-     float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut);
-
-    static const size_t m_pluginBlockSize;
-};
-
-#endif
-
--- a/audioio/AudioJACKTarget.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,402 +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.
-*/
-
-#ifdef HAVE_JACK
-
-#include "AudioJACKTarget.h"
-#include "AudioCallbackPlaySource.h"
-
-#include <iostream>
-#include <cmath>
-
-//#define DEBUG_AUDIO_JACK_TARGET 1
-
-#ifdef BUILD_STATIC
-#ifdef Q_OS_LINUX
-
-// Some lunacy to enable JACK support in static builds.  JACK isn't
-// supposed to be linked statically, because it depends on a
-// consistent shared memory layout between client library and daemon,
-// so it's very fragile in the face of version mismatches.
-//
-// Therefore for static builds on Linux we avoid linking against JACK
-// at all during the build, instead using dlopen and runtime symbol
-// lookup to switch on JACK support at runtime.  The following big
-// mess (down to the #endifs) is the code that implements this.
-
-static void *symbol(const char *name)
-{
-    static bool attempted = false;
-    static void *library = 0;
-    static std::map<const char *, void *> symbols;
-    if (symbols.find(name) != symbols.end()) return symbols[name];
-    if (!library) {
-        if (!attempted) {
-            library = ::dlopen("libjack.so.1", RTLD_NOW);
-            if (!library) library = ::dlopen("libjack.so.0", RTLD_NOW);
-            if (!library) library = ::dlopen("libjack.so", RTLD_NOW);
-            if (!library) {
-                std::cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: "
-                          << ::dlerror() << " (tried .so, .so.0, .so.1)"
-                          << std::endl;
-            }
-            attempted = true;
-        }
-        if (!library) return 0;
-    }
-    void *symbol = ::dlsym(library, name);
-    if (!symbol) {
-        std::cerr << "WARNING: AudioJACKTarget: Failed to locate symbol "
-                  << name << ": " << ::dlerror() << std::endl;
-    }
-    symbols[name] = symbol;
-    return symbol;
-}
-
-static int dynamic_jack_set_process_callback(jack_client_t *client,
-                                             JackProcessCallback process_callback,
-                                             void *arg)
-{
-    typedef int (*func)(jack_client_t *client,
-                        JackProcessCallback process_callback,
-                        void *arg);
-    void *s = symbol("jack_set_process_callback");
-    if (!s) return 1;
-    func f = (func)s;
-    return f(client, process_callback, arg);
-}
-
-static int dynamic_jack_set_xrun_callback(jack_client_t *client,
-                                          JackXRunCallback xrun_callback,
-                                          void *arg)
-{
-    typedef int (*func)(jack_client_t *client,
-                        JackXRunCallback xrun_callback,
-                        void *arg);
-    void *s = symbol("jack_set_xrun_callback");
-    if (!s) return 1;
-    func f = (func)s;
-    return f(client, xrun_callback, arg);
-}
-
-static const char **dynamic_jack_get_ports(jack_client_t *client, 
-                                           const char *port_name_pattern, 
-                                           const char *type_name_pattern, 
-                                           unsigned long flags)
-{
-    typedef const char **(*func)(jack_client_t *client, 
-                                 const char *port_name_pattern, 
-                                 const char *type_name_pattern, 
-                                 unsigned long flags);
-    void *s = symbol("jack_get_ports");
-    if (!s) return 0;
-    func f = (func)s;
-    return f(client, port_name_pattern, type_name_pattern, flags);
-}
-
-static jack_port_t *dynamic_jack_port_register(jack_client_t *client,
-                                               const char *port_name,
-                                               const char *port_type,
-                                               unsigned long flags,
-                                               unsigned long buffer_size)
-{
-    typedef jack_port_t *(*func)(jack_client_t *client,
-                                 const char *port_name,
-                                 const char *port_type,
-                                 unsigned long flags,
-                                 unsigned long buffer_size);
-    void *s = symbol("jack_port_register");
-    if (!s) return 0;
-    func f = (func)s;
-    return f(client, port_name, port_type, flags, buffer_size);
-}
-
-static int dynamic_jack_connect(jack_client_t *client,
-                                const char *source,
-                                const char *dest)
-{
-    typedef int (*func)(jack_client_t *client,
-                        const char *source,
-                        const char *dest);
-    void *s = symbol("jack_connect");
-    if (!s) return 1;
-    func f = (func)s;
-    return f(client, source, dest);
-}
-
-static void *dynamic_jack_port_get_buffer(jack_port_t *port,
-                                          jack_nframes_t sz)
-{
-    typedef void *(*func)(jack_port_t *, jack_nframes_t);
-    void *s = symbol("jack_port_get_buffer");
-    if (!s) return 0;
-    func f = (func)s;
-    return f(port, sz);
-}
-
-static int dynamic_jack_port_unregister(jack_client_t *client,
-                                        jack_port_t *port)
-{
-    typedef int(*func)(jack_client_t *, jack_port_t *);
-    void *s = symbol("jack_port_unregister");
-    if (!s) return 0;
-    func f = (func)s;
-    return f(client, port);
-}
-
-#define dynamic1(rv, name, argtype, failval) \
-    static rv dynamic_##name(argtype arg) { \
-        typedef rv (*func) (argtype); \
-        void *s = symbol(#name); \
-        if (!s) return failval; \
-        func f = (func) s; \
-        return f(arg); \
-    }
-
-dynamic1(jack_client_t *, jack_client_new, const char *, 0);
-dynamic1(jack_nframes_t, jack_get_buffer_size, jack_client_t *, 0);
-dynamic1(jack_nframes_t, jack_get_sample_rate, jack_client_t *, 0);
-dynamic1(int, jack_activate, jack_client_t *, 1);
-dynamic1(int, jack_deactivate, jack_client_t *, 1);
-dynamic1(int, jack_client_close, jack_client_t *, 1);
-dynamic1(jack_nframes_t, jack_port_get_latency, jack_port_t *, 0);
-dynamic1(const char *, jack_port_name, const jack_port_t *, 0);
-
-#define jack_client_new dynamic_jack_client_new
-#define jack_get_buffer_size dynamic_jack_get_buffer_size
-#define jack_get_sample_rate dynamic_jack_get_sample_rate
-#define jack_set_process_callback dynamic_jack_set_process_callback
-#define jack_set_xrun_callback dynamic_jack_set_xrun_callback
-#define jack_activate dynamic_jack_activate
-#define jack_deactivate dynamic_jack_deactivate
-#define jack_client_close dynamic_jack_client_close
-#define jack_get_ports dynamic_jack_get_ports
-#define jack_port_register dynamic_jack_port_register
-#define jack_port_unregister dynamic_jack_port_unregister
-#define jack_port_get_latency dynamic_jack_port_get_latency
-#define jack_port_name dynamic_jack_port_name
-#define jack_connect dynamic_jack_connect
-#define jack_port_get_buffer dynamic_jack_port_get_buffer
-
-#endif
-#endif
-
-AudioJACKTarget::AudioJACKTarget(AudioCallbackPlaySource *source) :
-    AudioCallbackPlayTarget(source),
-    m_client(0),
-    m_bufferSize(0),
-    m_sampleRate(0)
-{
-    char name[100];
-    strcpy(name, "Sonic Visualiser");
-    m_client = jack_client_new(name);
-
-    if (!m_client) {
-	sprintf(name, "Sonic Visualiser (%d)", (int)getpid());
-	m_client = jack_client_new(name);
-	if (!m_client) {
-	    std::cerr
-		<< "ERROR: AudioJACKTarget: Failed to connect to JACK server"
-		<< std::endl;
-	}
-    }
-
-    if (!m_client) return;
-
-    m_bufferSize = jack_get_buffer_size(m_client);
-    m_sampleRate = jack_get_sample_rate(m_client);
-
-    jack_set_xrun_callback(m_client, xrunStatic, this);
-    jack_set_process_callback(m_client, processStatic, this);
-
-    if (jack_activate(m_client)) {
-	std::cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
-		  << std::endl;
-    }
-
-    if (m_source) {
-	sourceModelReplaced();
-    }
-}
-
-AudioJACKTarget::~AudioJACKTarget()
-{
-    std::cerr << "AudioJACKTarget::~AudioJACKTarget()" << std::endl;
-    if (m_client) {
-	jack_deactivate(m_client);
-	jack_client_close(m_client);
-    }
-    std::cerr << "AudioJACKTarget::~AudioJACKTarget() done" << std::endl;
-}
-
-bool
-AudioJACKTarget::isOK() const
-{
-    return (m_client != 0);
-}
-
-int
-AudioJACKTarget::processStatic(jack_nframes_t nframes, void *arg)
-{
-    return ((AudioJACKTarget *)arg)->process(nframes);
-}
-
-int
-AudioJACKTarget::xrunStatic(void *arg)
-{
-    return ((AudioJACKTarget *)arg)->xrun();
-}
-
-void
-AudioJACKTarget::sourceModelReplaced()
-{
-    m_mutex.lock();
-
-    m_source->setTargetBlockSize(m_bufferSize);
-    m_source->setTargetSampleRate(m_sampleRate);
-
-    size_t channels = m_source->getSourceChannelCount();
-
-    // Because we offer pan, we always want at least 2 channels
-    if (channels < 2) channels = 2;
-
-    if (channels == m_outputs.size() || !m_client) {
-	m_mutex.unlock();
-	return;
-    }
-
-    const char **ports =
-	jack_get_ports(m_client, NULL, NULL,
-		       JackPortIsPhysical | JackPortIsInput);
-    size_t physicalPortCount = 0;
-    while (ports[physicalPortCount]) ++physicalPortCount;
-
-#ifdef DEBUG_AUDIO_JACK_TARGET    
-    std::cerr << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << std::endl;
-#endif
-
-    while (m_outputs.size() < channels) {
-	
-	char name[20];
-	jack_port_t *port;
-
-	sprintf(name, "out %d", m_outputs.size() + 1);
-
-	port = jack_port_register(m_client,
-				  name,
-				  JACK_DEFAULT_AUDIO_TYPE,
-				  JackPortIsOutput,
-				  0);
-
-	if (!port) {
-	    std::cerr
-		<< "ERROR: AudioJACKTarget: Failed to create JACK output port "
-		<< m_outputs.size() << std::endl;
-	} else {
-	    m_source->setTargetPlayLatency(jack_port_get_latency(port));
-	}
-
-	if (m_outputs.size() < physicalPortCount) {
-	    jack_connect(m_client, jack_port_name(port), ports[m_outputs.size()]);
-	}
-
-	m_outputs.push_back(port);
-    }
-
-    while (m_outputs.size() > channels) {
-	std::vector<jack_port_t *>::iterator itr = m_outputs.end();
-	--itr;
-	jack_port_t *port = *itr;
-	if (port) jack_port_unregister(m_client, port);
-	m_outputs.erase(itr);
-    }
-
-    m_mutex.unlock();
-}
-
-int
-AudioJACKTarget::process(jack_nframes_t nframes)
-{
-    if (!m_mutex.tryLock()) {
-	return 0;
-    }
-
-    if (m_outputs.empty()) {
-	m_mutex.unlock();
-	return 0;
-    }
-
-#ifdef DEBUG_AUDIO_JACK_TARGET    
-    std::cout << "AudioJACKTarget::process(" << nframes << "): have a source" << std::endl;
-#endif
-
-#ifdef DEBUG_AUDIO_JACK_TARGET    
-    if (m_bufferSize != nframes) {
-	std::cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << std::endl;
-    }
-#endif
-
-    float **buffers = (float **)alloca(m_outputs.size() * sizeof(float *));
-
-    for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
-	buffers[ch] = (float *)jack_port_get_buffer(m_outputs[ch], nframes);
-    }
-
-    size_t received = 0;
-
-    if (m_source) {
-	received = m_source->getSourceSamples(nframes, buffers);
-    }
-
-    for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
-        for (size_t i = received; i < nframes; ++i) {
-            buffers[ch][i] = 0.0;
-        }
-    }
-
-    float peakLeft = 0.0, peakRight = 0.0;
-
-    for (size_t ch = 0; ch < m_outputs.size(); ++ch) {
-
-	float peak = 0.0;
-
-	for (size_t i = 0; i < nframes; ++i) {
-	    buffers[ch][i] *= m_outputGain;
-	    float sample = fabsf(buffers[ch][i]);
-	    if (sample > peak) peak = sample;
-	}
-
-	if (ch == 0) peakLeft = peak;
-	if (ch > 0 || m_outputs.size() == 1) peakRight = peak;
-    }
-	    
-    if (m_source) {
-	m_source->setOutputLevels(peakLeft, peakRight);
-    }
-
-    m_mutex.unlock();
-    return 0;
-}
-
-int
-AudioJACKTarget::xrun()
-{
-    std::cerr << "AudioJACKTarget: xrun!" << std::endl;
-    if (m_source) m_source->audioProcessingOverload();
-    return 0;
-}
-
-#endif /* HAVE_JACK */
-
--- a/audioio/AudioJACKTarget.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +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 _AUDIO_JACK_TARGET_H_
-#define _AUDIO_JACK_TARGET_H_
-
-#ifdef HAVE_JACK
-
-#include <jack/jack.h>
-#include <vector>
-
-#include "AudioCallbackPlayTarget.h"
-
-#include <QMutex>
-
-class AudioCallbackPlaySource;
-
-class AudioJACKTarget : public AudioCallbackPlayTarget
-{
-    Q_OBJECT
-
-public:
-    AudioJACKTarget(AudioCallbackPlaySource *source);
-    virtual ~AudioJACKTarget();
-
-    virtual bool isOK() const;
-
-public slots:
-    virtual void sourceModelReplaced();
-
-protected:
-    int process(jack_nframes_t nframes);
-    int xrun();
-
-    static int processStatic(jack_nframes_t, void *);
-    static int xrunStatic(void *);
-
-    jack_client_t              *m_client;
-    std::vector<jack_port_t *>  m_outputs;
-    jack_nframes_t              m_bufferSize;
-    jack_nframes_t              m_sampleRate;
-    QMutex                      m_mutex;
-};
-
-#endif /* HAVE_JACK */
-
-#endif
-
--- a/audioio/AudioPortAudioTarget.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,254 +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.
-*/
-
-#ifdef HAVE_PORTAUDIO
-
-#include "AudioPortAudioTarget.h"
-#include "AudioCallbackPlaySource.h"
-
-#include <iostream>
-#include <cassert>
-#include <cmath>
-
-//#define DEBUG_AUDIO_PORT_AUDIO_TARGET 1
-
-AudioPortAudioTarget::AudioPortAudioTarget(AudioCallbackPlaySource *source) :
-    AudioCallbackPlayTarget(source),
-    m_stream(0),
-    m_bufferSize(0),
-    m_sampleRate(0),
-    m_latency(0)
-{
-    PaError err;
-
-#ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
-#ifdef HAVE_PORTAUDIO_V18
-    std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v18" << std::endl;
-#else
-    std::cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << std::endl;
-#endif
-#endif
-
-    err = Pa_Initialize();
-    if (err != paNoError) {
-	std::cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << std::endl;
-	return;
-    }
-
-    m_bufferSize = 1024;
-    m_sampleRate = 44100;
-    if (m_source && (m_source->getSourceSampleRate() != 0)) {
-	m_sampleRate = m_source->getSourceSampleRate();
-    }
-
-#ifdef HAVE_PORTAUDIO_V18
-    m_latency = Pa_GetMinNumBuffers(m_bufferSize, m_sampleRate) * m_bufferSize;
-#endif
-
-#ifdef HAVE_PORTAUDIO_V18
-    err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32,
-			       m_sampleRate, m_bufferSize, 0,
-			       processStatic, this);
-#else
-    err = Pa_OpenDefaultStream(&m_stream, 0, 2, paFloat32,
-			       m_sampleRate, m_bufferSize,
-			       processStatic, this);
-#endif    
-
-    if (err != paNoError) {
-	std::cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
-	m_stream = 0;
-	Pa_Terminate();
-	return;
-    }
-
-#ifndef HAVE_PORTAUDIO_V18
-    const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
-    m_latency = int(info->outputLatency * m_sampleRate + 0.001);
-#endif
-
-    std::cerr << "PortAudio latency = " << m_latency << " frames" << std::endl;
-
-    err = Pa_StartStream(m_stream);
-
-    if (err != paNoError) {
-	std::cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
-	Pa_CloseStream(m_stream);
-	m_stream = 0;
-	Pa_Terminate();
-	return;
-    }
-
-    if (m_source) {
-	std::cerr << "AudioPortAudioTarget: block size " << m_bufferSize << std::endl;
-	m_source->setTargetBlockSize(m_bufferSize);
-	m_source->setTargetSampleRate(m_sampleRate);
-	m_source->setTargetPlayLatency(m_latency);
-    }
-
-#ifdef DEBUG_PORT_AUDIO_TARGET
-    std::cerr << "AudioPortAudioTarget: initialised OK" << std::endl;
-#endif
-}
-
-AudioPortAudioTarget::~AudioPortAudioTarget()
-{
-    if (m_stream) {
-	PaError err;
-	err = Pa_CloseStream(m_stream);
-	if (err != paNoError) {
-	    std::cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << std::endl;
-	}
-	err = Pa_Terminate();
-        if (err != paNoError) {
-            std::cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << std::endl;
-	}   
-    }
-}
-
-bool
-AudioPortAudioTarget::isOK() const
-{
-    return (m_stream != 0);
-}
-
-#ifdef HAVE_PORTAUDIO_V18
-int
-AudioPortAudioTarget::processStatic(void *input, void *output,
-				    unsigned long nframes,
-				    PaTimestamp outTime, void *data)
-{
-    return ((AudioPortAudioTarget *)data)->process(input, output,
-						   nframes, outTime);
-}
-#else
-int
-AudioPortAudioTarget::processStatic(const void *input, void *output,
-                                    unsigned long nframes,
-                                    const PaStreamCallbackTimeInfo *timeInfo,
-                                    PaStreamCallbackFlags flags, void *data)
-{
-    return ((AudioPortAudioTarget *)data)->process(input, output,
-                                                   nframes, timeInfo,
-                                                   flags);
-}
-#endif
-
-void
-AudioPortAudioTarget::sourceModelReplaced()
-{
-    m_source->setTargetSampleRate(m_sampleRate);
-}
-
-#ifdef HAVE_PORTAUDIO_V18
-int
-AudioPortAudioTarget::process(void *inputBuffer, void *outputBuffer,
-			      unsigned long nframes,
-			      PaTimestamp)
-#else
-int
-AudioPortAudioTarget::process(const void *, void *outputBuffer,
-                              unsigned long nframes,
-                              const PaStreamCallbackTimeInfo *,
-                              PaStreamCallbackFlags)
-#endif
-{
-#ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET    
-    std::cout << "AudioPortAudioTarget::process(" << nframes << ")" << std::endl;
-#endif
-
-    if (!m_source) return 0;
-
-    float *output = (float *)outputBuffer;
-
-    assert(nframes <= m_bufferSize);
-
-    static float **tmpbuf = 0;
-    static size_t tmpbufch = 0;
-    static size_t tmpbufsz = 0;
-
-    size_t sourceChannels = m_source->getSourceChannelCount();
-
-    // Because we offer pan, we always want at least 2 channels
-    if (sourceChannels < 2) sourceChannels = 2;
-
-    if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < m_bufferSize) {
-
-	if (tmpbuf) {
-	    for (size_t i = 0; i < tmpbufch; ++i) {
-		delete[] tmpbuf[i];
-	    }
-	    delete[] tmpbuf;
-	}
-
-	tmpbufch = sourceChannels;
-	tmpbufsz = m_bufferSize;
-	tmpbuf = new float *[tmpbufch];
-
-	for (size_t i = 0; i < tmpbufch; ++i) {
-	    tmpbuf[i] = new float[tmpbufsz];
-	}
-    }
-	
-    size_t received = m_source->getSourceSamples(nframes, tmpbuf);
-
-    float peakLeft = 0.0, peakRight = 0.0;
-
-    for (size_t ch = 0; ch < 2; ++ch) {
-	
-	float peak = 0.0;
-
-	if (ch < sourceChannels) {
-
-	    // PortAudio samples are interleaved
-	    for (size_t i = 0; i < nframes; ++i) {
-                if (i < received) {
-                    output[i * 2 + ch] = tmpbuf[ch][i] * m_outputGain;
-                    float sample = fabsf(output[i * 2 + ch]);
-                    if (sample > peak) peak = sample;
-                } else {
-                    output[i * 2 + ch] = 0;
-                }
-	    }
-
-	} else if (ch == 1 && sourceChannels == 1) {
-
-	    for (size_t i = 0; i < nframes; ++i) {
-                if (i < received) {
-                    output[i * 2 + ch] = tmpbuf[0][i] * m_outputGain;
-                    float sample = fabsf(output[i * 2 + ch]);
-                    if (sample > peak) peak = sample;
-                } else {
-                    output[i * 2 + ch] = 0;
-                }
-	    }
-
-	} else {
-	    for (size_t i = 0; i < nframes; ++i) {
-		output[i * 2 + ch] = 0;
-	    }
-	}
-
-	if (ch == 0) peakLeft = peak;
-	if (ch > 0 || sourceChannels == 1) peakRight = peak;
-    }
-
-    m_source->setOutputLevels(peakLeft, peakRight);
-
-    return 0;
-}
-
-#endif /* HAVE_PORTAUDIO */
-
--- a/audioio/AudioPortAudioTarget.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +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 _AUDIO_PORT_AUDIO_TARGET_H_
-#define _AUDIO_PORT_AUDIO_TARGET_H_
-
-#ifdef HAVE_PORTAUDIO
-
-// This code can be compiled for either PortAudio v18 or v19.
-// PortAudio v19 is the default.  If you want to use v18, define
-// the preprocessor symbol HAVE_PORTAUDIO_v18.
-
-#include <portaudio.h>
-#include <vector>
-
-#include "AudioCallbackPlayTarget.h"
-
-class AudioCallbackPlaySource;
-
-class AudioPortAudioTarget : public AudioCallbackPlayTarget
-{
-    Q_OBJECT
-
-public:
-    AudioPortAudioTarget(AudioCallbackPlaySource *source);
-    virtual ~AudioPortAudioTarget();
-
-    virtual bool isOK() const;
-
-public slots:
-    virtual void sourceModelReplaced();
-
-protected:
-#ifdef HAVE_PORTAUDIO_V18
-
-    int process(void *input, void *output, unsigned long frames,
-		PaTimestamp outTime);
-
-    static int processStatic(void *, void *, unsigned long,
-			     PaTimestamp, void *);
-
-    PortAudioStream *m_stream;
-
-#else
-
-    int process(const void *input, void *output, unsigned long frames,
-                const PaStreamCallbackTimeInfo *timeInfo,
-                PaStreamCallbackFlags statusFlags);
-
-    static int processStatic(const void *, void *, unsigned long,
-                             const PaStreamCallbackTimeInfo *,
-                             PaStreamCallbackFlags, void *);
-
-    PaStream *m_stream;
-
-#endif
-
-    int m_bufferSize;
-    int m_sampleRate;
-    int m_latency;
-};
-
-#endif /* HAVE_PORTAUDIO */
-
-#endif
-
--- a/audioio/AudioTargetFactory.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +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 "AudioTargetFactory.h"
-
-#include "AudioJACKTarget.h"
-#include "AudioCoreAudioTarget.h"
-#include "AudioPortAudioTarget.h"
-
-#include <iostream>
-
-AudioCallbackPlayTarget *
-AudioTargetFactory::createCallbackTarget(AudioCallbackPlaySource *source)
-{
-    AudioCallbackPlayTarget *target = 0;
-
-#ifdef HAVE_JACK
-    target = new AudioJACKTarget(source);
-    if (target->isOK()) return target;
-    else {
-	std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open JACK target" << std::endl;
-	delete target;
-    }
-#endif
-
-#ifdef HAVE_COREAUDIO
-    target = new AudioCoreAudioTarget(source);
-    if (target->isOK()) return target;
-    else {
-	std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open CoreAudio target" << std::endl;
-	delete target;
-    }
-#endif
-
-#ifdef HAVE_DIRECTSOUND
-    target = new AudioDirectSoundTarget(source);
-    if (target->isOK()) return target;
-    else {
-	std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open DirectSound target" << std::endl;
-	delete target;
-    }
-#endif
-
-#ifdef HAVE_PORTAUDIO
-    target = new AudioPortAudioTarget(source);
-    if (target->isOK()) return target;
-    else {
-	std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open PortAudio target" << std::endl;
-	delete target;
-    }
-#endif
-
-    std::cerr << "WARNING: AudioTargetFactory::createCallbackTarget: No suitable targets available" << std::endl;
-    return 0;
-}
-
-
--- a/audioio/AudioTargetFactory.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,29 +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 _AUDIO_TARGET_FACTORY_H_
-#define _AUDIO_TARGET_FACTORY_H_
-
-class AudioCallbackPlaySource;
-class AudioCallbackPlayTarget;
-
-class AudioTargetFactory 
-{
-public:
-    static AudioCallbackPlayTarget *createCallbackTarget(AudioCallbackPlaySource *);
-};
-
-#endif
-
--- a/audioio/PhaseVocoderTimeStretcher.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,626 +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 "PhaseVocoderTimeStretcher.h"
-
-#include <iostream>
-#include <cassert>
-
-#include <QMutexLocker>
-
-//#define DEBUG_PHASE_VOCODER_TIME_STRETCHER 1
-
-PhaseVocoderTimeStretcher::PhaseVocoderTimeStretcher(size_t sampleRate,
-                                                     size_t channels,
-                                                     float ratio,
-                                                     bool sharpen,
-                                                     size_t maxOutputBlockSize) :
-    m_sampleRate(sampleRate),
-    m_channels(channels),
-    m_maxOutputBlockSize(maxOutputBlockSize),
-    m_ratio(ratio),
-    m_sharpen(sharpen),
-    m_totalCount(0),
-    m_transientCount(0),
-    m_n2sum(0),
-    m_mutex(new QMutex())
-{
-    initialise();
-}
-
-PhaseVocoderTimeStretcher::~PhaseVocoderTimeStretcher()
-{
-    std::cerr << "PhaseVocoderTimeStretcher::~PhaseVocoderTimeStretcher" << std::endl;
-
-    cleanup();
-    
-    delete m_mutex;
-}
-
-void
-PhaseVocoderTimeStretcher::initialise()
-{
-    std::cerr << "PhaseVocoderTimeStretcher::initialise" << std::endl;
-
-    calculateParameters();
-        
-    m_analysisWindow = new Window<float>(HanningWindow, m_wlen);
-    m_synthesisWindow = new Window<float>(HanningWindow, m_wlen);
-
-    m_prevPhase = new float *[m_channels];
-    m_prevAdjustedPhase = new float *[m_channels];
-
-    m_prevTransientMag = (float *)fftf_malloc(sizeof(float) * (m_wlen / 2 + 1));
-    m_prevTransientScore = 0;
-    m_prevTransient = false;
-
-    m_tempbuf = (float *)fftf_malloc(sizeof(float) * m_wlen);
-
-    m_time = new float *[m_channels];
-    m_freq = new fftf_complex *[m_channels];
-    m_plan = new fftf_plan[m_channels];
-    m_iplan = new fftf_plan[m_channels];
-
-    m_inbuf = new RingBuffer<float> *[m_channels];
-    m_outbuf = new RingBuffer<float> *[m_channels];
-    m_mashbuf = new float *[m_channels];
-
-    m_modulationbuf = (float *)fftf_malloc(sizeof(float) * m_wlen);
-        
-    for (size_t c = 0; c < m_channels; ++c) {
-
-        m_prevPhase[c] = (float *)fftf_malloc(sizeof(float) * (m_wlen / 2 + 1));
-        m_prevAdjustedPhase[c] = (float *)fftf_malloc(sizeof(float) * (m_wlen / 2 + 1));
-
-        m_time[c] = (float *)fftf_malloc(sizeof(float) * m_wlen);
-        m_freq[c] = (fftf_complex *)fftf_malloc(sizeof(fftf_complex) *
-                                                  (m_wlen / 2 + 1));
-        
-        m_plan[c] = fftf_plan_dft_r2c_1d(m_wlen, m_time[c], m_freq[c], FFTW_MEASURE);
-        m_iplan[c] = fftf_plan_dft_c2r_1d(m_wlen, m_freq[c], m_time[c], FFTW_MEASURE);
-
-        m_outbuf[c] = new RingBuffer<float>
-            ((m_maxOutputBlockSize + m_wlen) * 2);
-        m_inbuf[c] = new RingBuffer<float>
-            (lrintf(m_outbuf[c]->getSize() / m_ratio) + m_wlen);
-
-        std::cerr << "making inbuf size " << m_inbuf[c]->getSize() << " (outbuf size is " << m_outbuf[c]->getSize() << ", ratio " << m_ratio << ")" << std::endl;
-
-           
-        m_mashbuf[c] = (float *)fftf_malloc(sizeof(float) * m_wlen);
-        
-        for (size_t i = 0; i < m_wlen; ++i) {
-            m_mashbuf[c][i] = 0.0;
-        }
-
-        for (size_t i = 0; i <= m_wlen/2; ++i) {
-            m_prevPhase[c][i] = 0.0;
-            m_prevAdjustedPhase[c][i] = 0.0;
-        }
-    }
-
-    for (size_t i = 0; i < m_wlen; ++i) {
-        m_modulationbuf[i] = 0.0;
-    }
-
-    for (size_t i = 0; i <= m_wlen/2; ++i) {
-        m_prevTransientMag[i] = 0.0;
-    }
-}
-
-void
-PhaseVocoderTimeStretcher::calculateParameters()
-{
-    std::cerr << "PhaseVocoderTimeStretcher::calculateParameters" << std::endl;
-
-    m_wlen = 1024;
-
-    //!!! In transient sharpening mode, we need to pick the window
-    //length so as to be more or less fixed in audio duration (i.e. we
-    //need to exploit the sample rate)
-
-    //!!! have to work out the relationship between wlen and transient
-    //threshold
-
-    if (m_ratio < 1) {
-        if (m_ratio < 0.4) {
-            m_n1 = 1024;
-            m_wlen = 2048;
-        } else if (m_ratio < 0.8) {
-            m_n1 = 512;
-        } else {
-            m_n1 = 256;
-        }
-        if (shouldSharpen()) {
-            m_wlen = 2048;
-        }
-        m_n2 = lrintf(m_n1 * m_ratio);
-    } else {
-        if (m_ratio > 2) {
-            m_n2 = 512;
-            m_wlen = 4096; 
-        } else if (m_ratio > 1.6) {
-            m_n2 = 384;
-            m_wlen = 2048;
-        } else {
-            m_n2 = 256;
-        }
-        if (shouldSharpen()) {
-            if (m_wlen < 2048) m_wlen = 2048;
-        }
-        m_n1 = lrintf(m_n2 / m_ratio);
-        if (m_n1 == 0) {
-            m_n1 = 1;
-            m_n2 = lrintf(m_ratio);
-        }
-    }
-
-    m_transientThreshold = lrintf(m_wlen / 4.5);
-
-    m_totalCount = 0;
-    m_transientCount = 0;
-    m_n2sum = 0;
-
-
-    std::cerr << "PhaseVocoderTimeStretcher: channels = " << m_channels
-              << ", ratio = " << m_ratio
-              << ", n1 = " << m_n1 << ", n2 = " << m_n2 << ", wlen = "
-              << m_wlen << ", max = " << m_maxOutputBlockSize << std::endl;
-//              << ", outbuflen = " << m_outbuf[0]->getSize() << std::endl;
-}
-
-void
-PhaseVocoderTimeStretcher::cleanup()
-{
-    std::cerr << "PhaseVocoderTimeStretcher::cleanup" << std::endl;
-
-    for (size_t c = 0; c < m_channels; ++c) {
-
-        fftf_destroy_plan(m_plan[c]);
-        fftf_destroy_plan(m_iplan[c]);
-
-        fftf_free(m_time[c]);
-        fftf_free(m_freq[c]);
-
-        fftf_free(m_mashbuf[c]);
-        fftf_free(m_prevPhase[c]);
-        fftf_free(m_prevAdjustedPhase[c]);
-
-        delete m_inbuf[c];
-        delete m_outbuf[c];
-    }
-
-    fftf_free(m_tempbuf);
-    fftf_free(m_modulationbuf);
-    fftf_free(m_prevTransientMag);
-
-    delete[] m_prevPhase;
-    delete[] m_prevAdjustedPhase;
-    delete[] m_inbuf;
-    delete[] m_outbuf;
-    delete[] m_mashbuf;
-    delete[] m_time;
-    delete[] m_freq;
-    delete[] m_plan;
-    delete[] m_iplan;
-
-    delete m_analysisWindow;
-    delete m_synthesisWindow;
-}	
-
-void
-PhaseVocoderTimeStretcher::setRatio(float ratio)
-{
-    QMutexLocker locker(m_mutex);
-
-    size_t formerWlen = m_wlen;
-    m_ratio = ratio;
-
-    std::cerr << "PhaseVocoderTimeStretcher::setRatio: new ratio " << ratio
-              << std::endl;
-
-    calculateParameters();
-
-    if (m_wlen == formerWlen) {
-
-        // This is the only container whose size depends on m_ratio
-
-        RingBuffer<float> **newin = new RingBuffer<float> *[m_channels];
-
-        size_t formerSize = m_inbuf[0]->getSize();
-        size_t newSize = lrintf(m_outbuf[0]->getSize() / m_ratio) + m_wlen;
-
-        std::cerr << "resizing inbuf from " << formerSize << " to "
-                  << newSize << " (outbuf size is " << m_outbuf[0]->getSize() << ", ratio " << m_ratio << ")" << std::endl;
-
-        if (formerSize != newSize) {
-
-            size_t ready = m_inbuf[0]->getReadSpace();
-
-            for (size_t c = 0; c < m_channels; ++c) {
-                newin[c] = new RingBuffer<float>(newSize);
-            }
-
-            if (ready > 0) {
-
-                size_t copy = std::min(ready, newSize);
-                float *tmp = new float[ready];
-
-                for (size_t c = 0; c < m_channels; ++c) {
-                    m_inbuf[c]->read(tmp, ready);
-                    newin[c]->write(tmp + ready - copy, copy);
-                }
-                
-                delete[] tmp;
-            }
-            
-            for (size_t c = 0; c < m_channels; ++c) {
-                delete m_inbuf[c];
-            }
-            
-            delete[] m_inbuf;
-            m_inbuf = newin;
-        }
-
-    } else {
-        
-        std::cerr << "wlen changed" << std::endl;
-        cleanup();
-        initialise();
-    }
-}
-
-size_t
-PhaseVocoderTimeStretcher::getProcessingLatency() const
-{
-    return getWindowSize() - getInputIncrement();
-}
-
-size_t
-PhaseVocoderTimeStretcher::getRequiredInputSamples() const
-{
-    QMutexLocker locker(m_mutex);
-
-    if (m_inbuf[0]->getReadSpace() >= m_wlen) return 0;
-    return m_wlen - m_inbuf[0]->getReadSpace();
-}
-
-void
-PhaseVocoderTimeStretcher::putInput(float **input, size_t samples)
-{
-    QMutexLocker locker(m_mutex);
-
-    // We need to add samples from input to our internal buffer.  When
-    // we have m_windowSize samples in the buffer, we can process it,
-    // move the samples back by m_n1 and write the output onto our
-    // internal output buffer.  If we have (samples * ratio) samples
-    // in that, we can write m_n2 of them back to output and return
-    // (otherwise we have to write zeroes).
-
-    // When we process, we write m_wlen to our fixed output buffer
-    // (m_mashbuf).  We then pull out the first m_n2 samples from that
-    // buffer, push them into the output ring buffer, and shift
-    // m_mashbuf left by that amount.
-
-    // The processing latency is then m_wlen - m_n2.
-
-    size_t consumed = 0;
-
-    while (consumed < samples) {
-
-	size_t writable = m_inbuf[0]->getWriteSpace();
-	writable = std::min(writable, samples - consumed);
-
-	if (writable == 0) {
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-	    std::cerr << "WARNING: PhaseVocoderTimeStretcher::putInput: writable == 0 (inbuf has " << m_inbuf[0]->getReadSpace() << " samples available for reading, space for " << m_inbuf[0]->getWriteSpace() << " more)" << std::endl;
-#endif
-            if (m_inbuf[0]->getReadSpace() < m_wlen ||
-                m_outbuf[0]->getWriteSpace() < m_n2) {
-                std::cerr << "WARNING: PhaseVocoderTimeStretcher::putInput: Inbuf has " << m_inbuf[0]->getReadSpace() << ", outbuf has space for " << m_outbuf[0]->getWriteSpace() << " (n2 = " << m_n2 << ", wlen = " << m_wlen << "), won't be able to process" << std::endl;
-                break;
-            }
-	} else {
-
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-            std::cerr << "writing " << writable << " from index " << consumed << " to inbuf, consumed will be " << consumed + writable << std::endl;
-#endif
-
-            for (size_t c = 0; c < m_channels; ++c) {
-                m_inbuf[c]->write(input[c] + consumed, writable);
-            }
-            consumed += writable;
-        }
-
-	while (m_inbuf[0]->getReadSpace() >= m_wlen &&
-	       m_outbuf[0]->getWriteSpace() >= m_n2) {
-
-	    // We know we have at least m_wlen samples available
-	    // in m_inbuf.  We need to peek m_wlen of them for
-	    // processing, and then read m_n1 to advance the read
-	    // pointer.
-            
-            for (size_t c = 0; c < m_channels; ++c) {
-
-                size_t got = m_inbuf[c]->peek(m_tempbuf, m_wlen);
-                assert(got == m_wlen);
-
-                analyseBlock(c, m_tempbuf);
-            }
-
-            bool transient = false;
-            if (shouldSharpen()) transient = isTransient();
-
-            size_t n2 = m_n2;
-
-            if (transient) {
-                n2 = m_n1;
-            }
-
-            ++m_totalCount;
-            if (transient) ++m_transientCount;
-            m_n2sum += n2;
-
-//            std::cerr << "ratio for last 10: " <<last10num << "/" << (10 * m_n1) << " = " << float(last10num) / float(10 * m_n1) << " (should be " << m_ratio << ")" << std::endl;
-            
-            if (m_totalCount > 50 && m_transientCount < m_totalCount) {
-
-                int fixed = lrintf(m_transientCount * m_n1);
-
-                int idealTotal = lrintf(m_totalCount * m_n1 * m_ratio);
-                int idealSquashy = idealTotal - fixed;
-
-                int squashyCount = m_totalCount - m_transientCount;
-                
-                n2 = lrintf(idealSquashy / squashyCount);
-
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-                if (n2 != m_n2) {
-                    std::cerr << m_n2 << " -> " << n2 << std::endl;
-                }
-#endif
-            }
-
-            for (size_t c = 0; c < m_channels; ++c) {
-
-                synthesiseBlock(c, m_mashbuf[c],
-                                c == 0 ? m_modulationbuf : 0,
-                                m_prevTransient ? m_n1 : m_n2);
-
-
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-                std::cerr << "writing first " << m_n2 << " from mashbuf, skipping " << m_n1 << " on inbuf " << std::endl;
-#endif
-                m_inbuf[c]->skip(m_n1);
-
-                for (size_t i = 0; i < n2; ++i) {
-                    if (m_modulationbuf[i] > 0.f) {
-                        m_mashbuf[c][i] /= m_modulationbuf[i];
-                    }
-                }
-
-                m_outbuf[c]->write(m_mashbuf[c], n2);
-
-                for (size_t i = 0; i < m_wlen - n2; ++i) {
-                    m_mashbuf[c][i] = m_mashbuf[c][i + n2];
-                }
-
-                for (size_t i = m_wlen - n2; i < m_wlen; ++i) {
-                    m_mashbuf[c][i] = 0.0f;
-                }
-            }
-
-            m_prevTransient = transient;
-
-            for (size_t i = 0; i < m_wlen - n2; ++i) {
-                m_modulationbuf[i] = m_modulationbuf[i + n2];
-	    }
-
-	    for (size_t i = m_wlen - n2; i < m_wlen; ++i) {
-                m_modulationbuf[i] = 0.0f;
-	    }
-
-            if (!transient) m_n2 = n2;
-	}
-
-
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-	std::cerr << "loop ended: inbuf read space " << m_inbuf[0]->getReadSpace() << ", outbuf write space " << m_outbuf[0]->getWriteSpace() << std::endl;
-#endif
-    }
-
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-    std::cerr << "PhaseVocoderTimeStretcher::putInput returning" << std::endl;
-#endif
-
-//    std::cerr << "ratio: nominal: " << getRatio() << " actual: "
-//              << m_total2 << "/" << m_total1 << " = " << float(m_total2) / float(m_total1) << " ideal: " << m_ratio << std::endl;
-}
-
-size_t
-PhaseVocoderTimeStretcher::getAvailableOutputSamples() const
-{
-    QMutexLocker locker(m_mutex);
-
-    return m_outbuf[0]->getReadSpace();
-}
-
-void
-PhaseVocoderTimeStretcher::getOutput(float **output, size_t samples)
-{
-    QMutexLocker locker(m_mutex);
-
-    if (m_outbuf[0]->getReadSpace() < samples) {
-	std::cerr << "WARNING: PhaseVocoderTimeStretcher::getOutput: not enough data (yet?) (" << m_outbuf[0]->getReadSpace() << " < " << samples << ")" << std::endl;
-	size_t fill = samples - m_outbuf[0]->getReadSpace();
-        for (size_t c = 0; c < m_channels; ++c) {
-            for (size_t i = 0; i < fill; ++i) {
-                output[c][i] = 0.0;
-            }
-            m_outbuf[c]->read(output[c] + fill, m_outbuf[c]->getReadSpace());
-        }
-    } else {
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-	std::cerr << "enough data - writing " << samples << " from outbuf" << std::endl;
-#endif
-        for (size_t c = 0; c < m_channels; ++c) {
-            m_outbuf[c]->read(output[c], samples);
-        }
-    }
-
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-    std::cerr << "PhaseVocoderTimeStretcher::getOutput returning" << std::endl;
-#endif
-}
-
-void
-PhaseVocoderTimeStretcher::analyseBlock(size_t c, float *buf)
-{
-    size_t i;
-
-    // buf contains m_wlen samples
-
-#ifdef DEBUG_PHASE_VOCODER_TIME_STRETCHER
-    std::cerr << "PhaseVocoderTimeStretcher::analyseBlock (channel " << c << ")" << std::endl;
-#endif
-
-    m_analysisWindow->cut(buf);
-
-    for (i = 0; i < m_wlen/2; ++i) {
-	float temp = buf[i];
-	buf[i] = buf[i + m_wlen/2];
-	buf[i + m_wlen/2] = temp;
-    }
-
-    for (i = 0; i < m_wlen; ++i) {
-	m_time[c][i] = buf[i];
-    }
-
-    fftf_execute(m_plan[c]); // m_time -> m_freq
-}
-
-bool
-PhaseVocoderTimeStretcher::isTransient()
-{
-    int count = 0;
-
-    for (size_t i = 0; i <= m_wlen/2; ++i) {
-
-        float real = 0.f, imag = 0.f;
-
-        for (size_t c = 0; c < m_channels; ++c) {
-            real += m_freq[c][i][0];
-            imag += m_freq[c][i][1];
-        }
-
-        float sqrmag = (real * real + imag * imag);
-
-        if (m_prevTransientMag[i] > 0.f) {
-            float diff = 10.f * log10f(sqrmag / m_prevTransientMag[i]);
-            if (diff > 3.f) ++count;
-        }
-
-        m_prevTransientMag[i] = sqrmag;
-    }
-
-    bool isTransient = false;
-
-//    if (count > m_transientThreshold &&
-//        count > m_prevTransientScore * 1.2) {
-    if (count > m_prevTransientScore &&
-        count > m_transientThreshold &&
-        count - m_prevTransientScore > int(m_wlen) / 20) {
-        isTransient = true;
-
-
-//        std::cerr << "isTransient (count = " << count << ", prev = " << m_prevTransientScore << ", diff = " << count - m_prevTransientScore << ", ratio = " << (m_totalCount > 0 ? (float (m_n2sum) / float(m_totalCount * m_n1)) : 1.f) << ", ideal = " << m_ratio << ")" << std::endl;
-//    } else {
-//        std::cerr << " !transient (count = " << count << ", prev = " << m_prevTransientScore << ", diff = " << count - m_prevTransientScore << ")" << std::endl;
-    }
-
-    m_prevTransientScore = count;
-
-    return isTransient;
-}
-
-void
-PhaseVocoderTimeStretcher::synthesiseBlock(size_t c,
-                                           float *out,
-                                           float *modulation,
-                                           size_t lastStep)
-{
-    bool unchanged = (lastStep == m_n1);
-
-    for (size_t i = 0; i <= m_wlen/2; ++i) {
-		
-        float phase = princargf(atan2f(m_freq[c][i][1], m_freq[c][i][0]));
-        float adjustedPhase = phase;
-
-        if (!unchanged) {
-
-            float omega = (2 * M_PI * m_n1 * i) / m_wlen;
-	
-            float expectedPhase = m_prevPhase[c][i] + omega;
-
-            float phaseError = princargf(phase - expectedPhase);
-
-            float phaseIncrement = (omega + phaseError) / m_n1;
-            
-            adjustedPhase = m_prevAdjustedPhase[c][i] +
-                lastStep * phaseIncrement;
-            
-            float mag = sqrtf(m_freq[c][i][0] * m_freq[c][i][0] +
-                              m_freq[c][i][1] * m_freq[c][i][1]);
-            
-            float real = mag * cosf(adjustedPhase);
-            float imag = mag * sinf(adjustedPhase);
-            m_freq[c][i][0] = real;
-            m_freq[c][i][1] = imag;
-        }
-
-        m_prevPhase[c][i] = phase;
-        m_prevAdjustedPhase[c][i] = adjustedPhase;
-    }
-
-    fftf_execute(m_iplan[c]); // m_freq -> m_time, inverse fft
-
-    for (size_t i = 0; i < m_wlen/2; ++i) {
-        float temp = m_time[c][i];
-        m_time[c][i] = m_time[c][i + m_wlen/2];
-        m_time[c][i + m_wlen/2] = temp;
-    }
-    
-    for (size_t i = 0; i < m_wlen; ++i) {
-        m_time[c][i] = m_time[c][i] / m_wlen;
-    }
-
-    m_synthesisWindow->cut(m_time[c]);
-
-    for (size_t i = 0; i < m_wlen; ++i) {
-        out[i] += m_time[c][i];
-    }
-
-    if (modulation) {
-
-        float area = m_analysisWindow->getArea();
-
-        for (size_t i = 0; i < m_wlen; ++i) {
-            float val = m_synthesisWindow->getValue(i);
-            modulation[i] += val * area;
-        }
-    }
-}
-
-
--- a/audioio/PhaseVocoderTimeStretcher.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,187 +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 _PHASE_VOCODER_TIME_STRETCHER_H_
-#define _PHASE_VOCODER_TIME_STRETCHER_H_
-
-#include "base/Window.h"
-#include "base/RingBuffer.h"
-
-#include "data/fft/FFTapi.h"
-
-#include <QMutex>
-
-/**
- * A time stretcher that alters the performance speed of audio,
- * preserving pitch.
- *
- * This is based on the straightforward phase vocoder with phase
- * unwrapping (as in e.g. the DAFX book pp275-), with optional
- * percussive transient detection to avoid smearing percussive notes
- * and resynchronise phases, and adding a stream API for real-time
- * use.  Principles and methods from Chris Duxbury, AES 2002 and 2004
- * thesis; Emmanuel Ravelli, DAFX 2005; Dan Barry, ISSC 2005 on
- * percussion detection; code by Chris Cannam.
- */
-
-class PhaseVocoderTimeStretcher
-{
-public:
-    PhaseVocoderTimeStretcher(size_t sampleRate,
-                              size_t channels,
-                              float ratio,
-                              bool sharpen,
-                              size_t maxOutputBlockSize);
-    virtual ~PhaseVocoderTimeStretcher();
-
-    /**
-     * Return the number of samples that would need to be added via
-     * putInput in order to provoke the time stretcher into doing some
-     * time stretching and making more output samples available.
-     * This will be an estimate, if transient sharpening is on; the 
-     * caller may need to do the put/get/test cycle more than once.
-     */
-    size_t getRequiredInputSamples() const;
-
-    /**
-     * Put (and possibly process) a given number of input samples.
-     * Number should usually equal the value returned from
-     * getRequiredInputSamples().
-     */
-    void putInput(float **input, size_t samples);
-
-    /**
-     * Get the number of processed samples ready for reading.
-     */
-    size_t getAvailableOutputSamples() const;
-
-    /**
-     * Get some processed samples.
-     */
-    void getOutput(float **output, size_t samples);
-
-    //!!! and reset?
-
-    /**
-     * Change the time stretch ratio.
-     */
-    void setRatio(float ratio);
-
-    /**
-     * Get the hop size for input.
-     */
-    size_t getInputIncrement() const { return m_n1; }
-
-    /**
-     * Get the hop size for output.
-     */
-    size_t getOutputIncrement() const { return m_n2; }
-
-    /**
-     * Get the window size for FFT processing.
-     */
-    size_t getWindowSize() const { return m_wlen; }
-
-    /**
-     * Get the stretch ratio.
-     */
-    float getRatio() const { return float(m_n2) / float(m_n1); }
-
-    /**
-     * Return whether this time stretcher will attempt to sharpen transients.
-     */
-    bool getSharpening() const { return m_sharpen; }
-
-    /**
-     * Return the number of channels for this time stretcher.
-     */
-    size_t getChannelCount() const { return m_channels; }
-
-    /**
-     * Get the latency added by the time stretcher, in sample frames.
-     * This will be exact if transient sharpening is off, or approximate
-     * if it is on.
-     */
-    size_t getProcessingLatency() const;
-
-protected:
-    /**
-     * Process a single phase vocoder frame from "in" into
-     * m_freq[channel].
-     */
-    void analyseBlock(size_t channel, float *in); // into m_freq[channel]
-
-    /**
-     * Examine m_freq[0..m_channels-1] and return whether a percussive
-     * transient is found.
-     */
-    bool isTransient(); 
-
-    /**
-     * Resynthesise from m_freq[channel] adding in to "out",
-     * adjusting phases on the basis of a prior step size of lastStep.
-     * Also add the window shape in to the modulation array (if
-     * present) -- for use in ensuring the output has the correct
-     * magnitude afterwards.
-     */
-    void synthesiseBlock(size_t channel, float *out, float *modulation,
-                         size_t lastStep);
-
-    void initialise();
-    void calculateParameters();
-    void cleanup();
-
-    bool shouldSharpen() {
-        return m_sharpen && (m_ratio > 0.25);
-    }
-
-    size_t m_sampleRate;
-    size_t m_channels;
-    size_t m_maxOutputBlockSize;
-    float m_ratio;
-    bool m_sharpen;
-    size_t m_n1;
-    size_t m_n2;
-    size_t m_wlen;
-    Window<float> *m_analysisWindow;
-    Window<float> *m_synthesisWindow;
-
-    int m_totalCount;
-    int m_transientCount;
-    int m_n2sum;
-
-    float **m_prevPhase;
-    float **m_prevAdjustedPhase;
-
-    float *m_prevTransientMag;
-    int  m_prevTransientScore;
-    int  m_transientThreshold;
-    bool m_prevTransient;
-
-    float *m_tempbuf;
-    float **m_time;
-    fftf_complex **m_freq;
-    fftf_plan *m_plan;
-    fftf_plan *m_iplan;
-    
-    RingBuffer<float> **m_inbuf;
-    RingBuffer<float> **m_outbuf;
-    float **m_mashbuf;
-    float *m_modulationbuf;
-
-    QMutex *m_mutex;
-};
-
-#endif
--- a/audioio/PlaySpeedRangeMapper.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,133 +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 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 "PlaySpeedRangeMapper.h"
-
-#include <iostream>
-#include <cmath>
-
-PlaySpeedRangeMapper::PlaySpeedRangeMapper(int minpos, int maxpos) :
-    m_minpos(minpos),
-    m_maxpos(maxpos)
-{
-}
-
-int
-PlaySpeedRangeMapper::getPositionForValue(float value) const
-{
-    // value is percent
-    float factor = getFactorForValue(value);
-    int position = getPositionForFactor(factor);
-    return position;
-}
-
-int
-PlaySpeedRangeMapper::getPositionForFactor(float factor) const
-{
-    bool slow = (factor > 1.0);
-
-    if (!slow) factor = 1.0 / factor;
-    
-    int half = (m_maxpos + m_minpos) / 2;
-
-    factor = sqrtf((factor - 1.0) * 1000.f);
-    int position = lrintf(((factor * (half - m_minpos)) / 100.0) + m_minpos);
-
-    if (slow) {
-        position = half - position;
-    } else {
-        position = position + half;
-    }
-
-//    std::cerr << "value = " << value << " slow = " << slow << " factor = " << factor << " position = " << position << std::endl;
-
-    return position;
-}
-
-float
-PlaySpeedRangeMapper::getValueForPosition(int position) const
-{
-    float factor = getFactorForPosition(position);
-    float pc = getValueForFactor(factor);
-    return pc;
-}
-
-float
-PlaySpeedRangeMapper::getValueForFactor(float factor) const
-{
-    float pc;
-    if (factor < 1.0) pc = ((1.0 / factor) - 1.0) * 100.0;
-    else pc = (1.0 - factor) * 100.0;
-//    std::cerr << "position = " << position << " percent = " << pc << std::endl;
-    return pc;
-}
-
-float
-PlaySpeedRangeMapper::getFactorForValue(float value) const
-{
-    // value is percent
-    
-    float factor;
-
-    if (value <= 0) {
-        factor = 1.0 - (value / 100.0);
-    } else {
-        factor = 1.0 / (1.0 + (value / 100.0));
-    }
-
-//    std::cerr << "value = " << value << " factor = " << factor << std::endl;
-    return factor;
-}
-
-float
-PlaySpeedRangeMapper::getFactorForPosition(int position) const
-{
-    bool slow = false;
-
-    if (position < m_minpos) position = m_minpos;
-    if (position > m_maxpos) position = m_maxpos;
-
-    int half = (m_maxpos + m_minpos) / 2;
-
-    if (position < half) {
-        slow = true;
-        position = half - position;
-    } else {
-        position = position - half;
-    }
-
-    // position is between min and half (inclusive)
-
-    float factor;
-
-    if (position == m_minpos) {
-        factor = 1.0;
-    } else {
-        factor = ((position - m_minpos) * 100.0) / (half - m_minpos);
-        factor = 1.0 + (factor * factor) / 1000.f;
-    }
-
-    if (!slow) factor = 1.0 / factor;
-
-//    std::cerr << "position = " << position << " slow = " << slow << " factor = " << factor << std::endl;
-
-    return factor;
-}
-
-QString
-PlaySpeedRangeMapper::getUnit() const
-{
-    return "%";
-}
--- a/audioio/PlaySpeedRangeMapper.h	Wed Oct 24 16:00:30 2007 +0000
+++ /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 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 _PLAY_SPEED_RANGE_MAPPER_H_
-#define _PLAY_SPEED_RANGE_MAPPER_H_
-
-#include "base/RangeMapper.h"
-
-class PlaySpeedRangeMapper : public RangeMapper
-{
-public:
-    PlaySpeedRangeMapper(int minpos, int maxpos);
-
-    virtual int getPositionForValue(float value) const;
-    virtual float getValueForPosition(int position) const;
-
-    int getPositionForFactor(float factor) const;
-    float getValueForFactor(float factor) const;
-
-    float getFactorForPosition(int position) const;
-    float getFactorForValue(float value) const;
-
-    virtual QString getUnit() const;
-    
-protected:
-    int m_minpos;
-    int m_maxpos;
-};
-
-
-#endif
--- a/document/Document.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,949 +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 "Document.h"
-
-#include "data/model/WaveFileModel.h"
-#include "data/model/WritableWaveFileModel.h"
-#include "data/model/DenseThreeDimensionalModel.h"
-#include "data/model/DenseTimeValueModel.h"
-#include "layer/Layer.h"
-#include "base/CommandHistory.h"
-#include "base/Command.h"
-#include "view/View.h"
-#include "base/PlayParameterRepository.h"
-#include "base/PlayParameters.h"
-#include "transform/TransformFactory.h"
-#include <QApplication>
-#include <QTextStream>
-#include <iostream>
-
-// For alignment:
-#include "data/model/AggregateWaveModel.h"
-#include "data/model/SparseTimeValueModel.h"
-#include "data/model/AlignmentModel.h"
-
-//!!! still need to handle command history, documentRestored/documentModified
-
-Document::Document() :
-    m_mainModel(0)
-{
-    connect(this, SIGNAL(modelAboutToBeDeleted(Model *)),
-            TransformFactory::getInstance(),
-            SLOT(modelAboutToBeDeleted(Model *)));
-}
-
-Document::~Document()
-{
-    //!!! Document should really own the command history.  atm we
-    //still refer to it in various places that don't have access to
-    //the document, be nice to fix that
-
-//    std::cerr << "\n\nDocument::~Document: about to clear command history" << std::endl;
-    CommandHistory::getInstance()->clear();
-    
-//    std::cerr << "Document::~Document: about to delete layers" << std::endl;
-    while (!m_layers.empty()) {
-	deleteLayer(*m_layers.begin(), true);
-    }
-
-    if (!m_models.empty()) {
-	std::cerr << "Document::~Document: WARNING: " 
-		  << m_models.size() << " model(s) still remain -- "
-		  << "should have been garbage collected when deleting layers"
-		  << std::endl;
-	while (!m_models.empty()) {
-            Model *model = m_models.begin()->first;
-	    if (model == m_mainModel) {
-		// just in case!
-		std::cerr << "Document::~Document: WARNING: Main model is also"
-			  << " in models list!" << std::endl;
-	    } else if (model) {
-		emit modelAboutToBeDeleted(model);
-                model->aboutToDelete();
-		delete model;
-	    }
-	    m_models.erase(m_models.begin());
-	}
-    }
-
-//    std::cerr << "Document::~Document: About to get rid of main model"
-//	      << std::endl;
-    if (m_mainModel) {
-        emit modelAboutToBeDeleted(m_mainModel);
-        m_mainModel->aboutToDelete();
-    }
-
-    emit mainModelChanged(0);
-    delete m_mainModel;
-
-}
-
-Layer *
-Document::createLayer(LayerFactory::LayerType type)
-{
-    Layer *newLayer = LayerFactory::getInstance()->createLayer(type);
-    if (!newLayer) return 0;
-
-    newLayer->setObjectName(getUniqueLayerName(newLayer->objectName()));
-
-    m_layers.insert(newLayer);
-    emit layerAdded(newLayer);
-
-    return newLayer;
-}
-
-Layer *
-Document::createMainModelLayer(LayerFactory::LayerType type)
-{
-    Layer *newLayer = createLayer(type);
-    if (!newLayer) return 0;
-    setModel(newLayer, m_mainModel);
-    return newLayer;
-}
-
-Layer *
-Document::createImportedLayer(Model *model)
-{
-    LayerFactory::LayerTypeSet types =
-	LayerFactory::getInstance()->getValidLayerTypes(model);
-
-    if (types.empty()) {
-	std::cerr << "WARNING: Document::importLayer: no valid display layer for model" << std::endl;
-	return 0;
-    }
-
-    //!!! for now, just use the first suitable layer type
-    LayerFactory::LayerType type = *types.begin();
-
-    Layer *newLayer = LayerFactory::getInstance()->createLayer(type);
-    if (!newLayer) return 0;
-
-    newLayer->setObjectName(getUniqueLayerName(newLayer->objectName()));
-
-    addImportedModel(model);
-    setModel(newLayer, model);
-
-    //!!! and all channels
-    setChannel(newLayer, -1);
-
-    m_layers.insert(newLayer);
-    emit layerAdded(newLayer);
-    return newLayer;
-}
-
-Layer *
-Document::createEmptyLayer(LayerFactory::LayerType type)
-{
-    Model *newModel =
-	LayerFactory::getInstance()->createEmptyModel(type, m_mainModel);
-    if (!newModel) return 0;
-
-    Layer *newLayer = createLayer(type);
-    if (!newLayer) {
-	delete newModel;
-	return 0;
-    }
-
-    addImportedModel(newModel);
-    setModel(newLayer, newModel);
-
-    return newLayer;
-}
-
-Layer *
-Document::createDerivedLayer(LayerFactory::LayerType type,
-			     TransformId transform)
-{
-    Layer *newLayer = createLayer(type);
-    if (!newLayer) return 0;
-
-    newLayer->setObjectName(getUniqueLayerName
-                            (TransformFactory::getInstance()->
-                             getTransformFriendlyName(transform)));
-
-    return newLayer;
-}
-
-Layer *
-Document::createDerivedLayer(TransformId transform,
-                             Model *inputModel, 
-                             const PluginTransform::ExecutionContext &context,
-                             QString configurationXml)
-{
-    Model *newModel = addDerivedModel(transform, inputModel,
-                                      context, configurationXml);
-    if (!newModel) {
-        // error already printed to stderr by addDerivedModel
-        emit modelGenerationFailed(transform);
-        return 0;
-    }
-
-    LayerFactory::LayerTypeSet types =
-	LayerFactory::getInstance()->getValidLayerTypes(newModel);
-
-    if (types.empty()) {
-	std::cerr << "WARNING: Document::createLayerForTransform: no valid display layer for output of transform " << transform.toStdString() << std::endl;
-	delete newModel;
-	return 0;
-    }
-
-    //!!! for now, just use the first suitable layer type
-
-    Layer *newLayer = createLayer(*types.begin());
-    setModel(newLayer, newModel);
-
-    //!!! We need to clone the model when adding the layer, so that it
-    //can be edited without affecting other layers that are based on
-    //the same model.  Unfortunately we can't just clone it now,
-    //because it probably hasn't been completed yet -- the transform
-    //runs in the background.  Maybe the transform has to handle
-    //cloning and cacheing models itself.
-    //
-    // Once we do clone models here, of course, we'll have to avoid
-    // leaking them too.
-    //
-    // We want the user to be able to add a model to a second layer
-    // _while it's still being calculated in the first_ and have it
-    // work quickly.  That means we need to put the same physical
-    // model pointer in both layers, so they can't actually be cloned.
-    
-    if (newLayer) {
-	newLayer->setObjectName(getUniqueLayerName
-                                (TransformFactory::getInstance()->
-                                 getTransformFriendlyName(transform)));
-    }
-
-    emit layerAdded(newLayer);
-    return newLayer;
-}
-
-void
-Document::setMainModel(WaveFileModel *model)
-{
-    Model *oldMainModel = m_mainModel;
-    m_mainModel = model;
-
-    emit modelAdded(m_mainModel);
-
-    std::vector<Layer *> obsoleteLayers;
-    std::set<QString> failedTransforms;
-
-    // We need to ensure that no layer is left using oldMainModel or
-    // any of the old derived models as its model.  Either replace the
-    // model, or delete the layer for each layer that is currently
-    // using one of these.  Carry out this replacement before we
-    // delete any of the models.
-
-    for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
-
-	Layer *layer = *i;
-	Model *model = layer->getModel();
-
-//        std::cerr << "Document::setMainModel: inspecting model "
-//                  << (model ? model->objectName().toStdString() : "(null)") << " in layer "
-//                  << layer->objectName().toStdString() << std::endl;
-
-	if (model == oldMainModel) {
-//            std::cerr << "... it uses the old main model, replacing" << std::endl;
-	    LayerFactory::getInstance()->setModel(layer, m_mainModel);
-	    continue;
-	}
-
-	if (m_models.find(model) == m_models.end()) {
-	    std::cerr << "WARNING: Document::setMainModel: Unknown model "
-		      << model << " in layer " << layer << std::endl;
-	    // get rid of this hideous degenerate
-	    obsoleteLayers.push_back(layer);
-	    continue;
-	}
-	    
-	if (m_models[model].source == oldMainModel) {
-
-//            std::cerr << "... it uses a model derived from the old main model, regenerating" << std::endl;
-
-	    // This model was derived from the previous main
-	    // model: regenerate it.
-	    
-	    TransformId transform = m_models[model].transform;
-            PluginTransform::ExecutionContext context = m_models[model].context;
-	    
-	    Model *replacementModel =
-                addDerivedModel(transform,
-                                m_mainModel,
-                                context,
-                                m_models[model].configurationXml);
-	    
-	    if (!replacementModel) {
-		std::cerr << "WARNING: Document::setMainModel: Failed to regenerate model for transform \""
-			  << transform.toStdString() << "\"" << " in layer " << layer << std::endl;
-                if (failedTransforms.find(transform) == failedTransforms.end()) {
-                    emit modelRegenerationFailed(layer->objectName(),
-                                                 transform);
-                    failedTransforms.insert(transform);
-                }
-		obsoleteLayers.push_back(layer);
-	    } else {
-                std::cerr << "Replacing model " << model << " (type "
-                          << typeid(*model).name() << ") with model "
-                          << replacementModel << " (type "
-                          << typeid(*replacementModel).name() << ") in layer "
-                          << layer << " (name " << layer->objectName().toStdString() << ")"
-                          << std::endl;
-                RangeSummarisableTimeValueModel *rm =
-                    dynamic_cast<RangeSummarisableTimeValueModel *>(replacementModel);
-                if (rm) {
-                    std::cerr << "new model has " << rm->getChannelCount() << " channels " << std::endl;
-                } else {
-                    std::cerr << "new model is not a RangeSummarisableTimeValueModel!" << std::endl;
-                }
-		setModel(layer, replacementModel);
-	    }
-	}	    
-    }
-
-    for (size_t k = 0; k < obsoleteLayers.size(); ++k) {
-	deleteLayer(obsoleteLayers[k], true);
-    }
-
-    emit mainModelChanged(m_mainModel);
-
-    // we already emitted modelAboutToBeDeleted for this
-    delete oldMainModel;
-}
-
-void
-Document::addDerivedModel(TransformId transform,
-                          Model *inputModel,
-                          const PluginTransform::ExecutionContext &context,
-                          Model *outputModelToAdd,
-                          QString configurationXml)
-{
-    if (m_models.find(outputModelToAdd) != m_models.end()) {
-	std::cerr << "WARNING: Document::addDerivedModel: Model already added"
-		  << std::endl;
-	return;
-    }
-
-//    std::cerr << "Document::addDerivedModel: source is " << inputModel << " \"" << inputModel->objectName().toStdString() << "\"" << std::endl;
-
-    ModelRecord rec;
-    rec.source = inputModel;
-    rec.transform = transform;
-    rec.context = context;
-    rec.configurationXml = configurationXml;
-    rec.refcount = 0;
-
-    outputModelToAdd->setSourceModel(inputModel);
-
-    m_models[outputModelToAdd] = rec;
-
-    emit modelAdded(outputModelToAdd);
-}
-
-
-void
-Document::addImportedModel(Model *model)
-{
-    if (m_models.find(model) != m_models.end()) {
-	std::cerr << "WARNING: Document::addImportedModel: Model already added"
-		  << std::endl;
-	return;
-    }
-
-    ModelRecord rec;
-    rec.source = 0;
-    rec.transform = "";
-    rec.refcount = 0;
-
-    m_models[model] = rec;
-
-    emit modelAdded(model);
-}
-
-Model *
-Document::addDerivedModel(TransformId transform,
-                          Model *inputModel,
-                          const PluginTransform::ExecutionContext &context,
-                          QString configurationXml)
-{
-    Model *model = 0;
-
-    for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
-	if (i->second.transform == transform &&
-	    i->second.source == inputModel && 
-            i->second.context == context &&
-            i->second.configurationXml == configurationXml) {
-	    return i->first;
-	}
-    }
-
-    model = TransformFactory::getInstance()->transform
-	(transform, inputModel, context, configurationXml);
-
-    if (!model) {
-	std::cerr << "WARNING: Document::addDerivedModel: no output model for transform " << transform.toStdString() << std::endl;
-    } else {
-	addDerivedModel(transform, inputModel, context, model, configurationXml);
-    }
-
-    return model;
-}
-
-void
-Document::releaseModel(Model *model) // Will _not_ release main model!
-{
-    if (model == 0) {
-	return;
-    }
-
-    if (model == m_mainModel) {
-	return;
-    }
-
-    bool toDelete = false;
-
-    if (m_models.find(model) != m_models.end()) {
-	
-	if (m_models[model].refcount == 0) {
-	    std::cerr << "WARNING: Document::releaseModel: model " << model
-		      << " reference count is zero already!" << std::endl;
-	} else {
-	    if (--m_models[model].refcount == 0) {
-		toDelete = true;
-	    }
-	}
-    } else { 
-	std::cerr << "WARNING: Document::releaseModel: Unfound model "
-		  << model << std::endl;
-	toDelete = true;
-    }
-
-    if (toDelete) {
-
-	int sourceCount = 0;
-
-	for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
-	    if (i->second.source == model) {
-		++sourceCount;
-		i->second.source = 0;
-	    }
-	}
-
-	if (sourceCount > 0) {
-	    std::cerr << "Document::releaseModel: Deleting model "
-		      << model << " even though it is source for "
-		      << sourceCount << " other derived model(s) -- resetting "
-		      << "their source fields appropriately" << std::endl;
-	}
-
-	emit modelAboutToBeDeleted(model);
-        model->aboutToDelete();
-	m_models.erase(model);
-	delete model;
-    }
-}
-
-void
-Document::deleteLayer(Layer *layer, bool force)
-{
-    if (m_layerViewMap.find(layer) != m_layerViewMap.end() &&
-	m_layerViewMap[layer].size() > 0) {
-
-	std::cerr << "WARNING: Document::deleteLayer: Layer "
-		  << layer << " [" << layer->objectName().toStdString() << "]"
-		  << " is still used in " << m_layerViewMap[layer].size()
-		  << " views!" << std::endl;
-
-	if (force) {
-
-	    std::cerr << "(force flag set -- deleting from all views)" << std::endl;
-
-	    for (std::set<View *>::iterator j = m_layerViewMap[layer].begin();
-		 j != m_layerViewMap[layer].end(); ++j) {
-		// don't use removeLayerFromView, as it issues a command
-		layer->setLayerDormant(*j, true);
-		(*j)->removeLayer(layer);
-	    }
-	    
-	    m_layerViewMap.erase(layer);
-
-	} else {
-	    return;
-	}
-    }
-
-    if (m_layers.find(layer) == m_layers.end()) {
-	std::cerr << "Document::deleteLayer: Layer "
-		  << layer << " does not exist, or has already been deleted "
-		  << "(this may not be as serious as it sounds)" << std::endl;
-	return;
-    }
-
-    m_layers.erase(layer);
-
-    releaseModel(layer->getModel());
-    emit layerRemoved(layer);
-    emit layerAboutToBeDeleted(layer);
-    delete layer;
-}
-
-void
-Document::setModel(Layer *layer, Model *model)
-{
-    if (model && 
-	model != m_mainModel &&
-	m_models.find(model) == m_models.end()) {
-	std::cerr << "ERROR: Document::setModel: Layer " << layer
-		  << " (\"" << layer->objectName().toStdString()
-                  << "\") wants to use unregistered model " << model
-		  << ": register the layer's model before setting it!"
-		  << std::endl;
-	return;
-    }
-
-    Model *previousModel = layer->getModel();
-
-    if (previousModel == model) {
-        std::cerr << "WARNING: Document::setModel: Layer " << layer << " (\""
-                  << layer->objectName().toStdString()
-                  << "\") is already set to model "
-                  << model << " (\""
-                  << (model ? model->objectName().toStdString() : "(null)")
-                  << "\")" << std::endl;
-        return;
-    }
-
-    if (model && model != m_mainModel) {
-	m_models[model].refcount ++;
-    }
-
-    if (model && previousModel) {
-        PlayParameterRepository::getInstance()->copyParameters
-            (previousModel, model);
-    }
-
-    LayerFactory::getInstance()->setModel(layer, model);
-
-    if (previousModel) {
-        releaseModel(previousModel);
-    }
-}
-
-void
-Document::setChannel(Layer *layer, int channel)
-{
-    LayerFactory::getInstance()->setChannel(layer, channel);
-}
-
-void
-Document::addLayerToView(View *view, Layer *layer)
-{
-    Model *model = layer->getModel();
-    if (!model) {
-//	std::cerr << "Document::addLayerToView: Layer (\""
-//                  << layer->objectName().toStdString()
-//                  << "\") with no model being added to view: "
-//                  << "normally you want to set the model first" << std::endl;
-    } else {
-	if (model != m_mainModel &&
-	    m_models.find(model) == m_models.end()) {
-	    std::cerr << "ERROR: Document::addLayerToView: Layer " << layer
-		      << " has unregistered model " << model
-		      << " -- register the layer's model before adding the layer!" << std::endl;
-	    return;
-	}
-    }
-
-    CommandHistory::getInstance()->addCommand
-	(new Document::AddLayerCommand(this, view, layer));
-}
-
-void
-Document::removeLayerFromView(View *view, Layer *layer)
-{
-    CommandHistory::getInstance()->addCommand
-	(new Document::RemoveLayerCommand(this, view, layer));
-}
-
-void
-Document::addToLayerViewMap(Layer *layer, View *view)
-{
-    bool firstView = (m_layerViewMap.find(layer) == m_layerViewMap.end() ||
-                      m_layerViewMap[layer].empty());
-
-    if (m_layerViewMap[layer].find(view) !=
-	m_layerViewMap[layer].end()) {
-	std::cerr << "WARNING: Document::addToLayerViewMap:"
-		  << " Layer " << layer << " -> view " << view << " already in"
-		  << " layer view map -- internal inconsistency" << std::endl;
-    }
-
-    m_layerViewMap[layer].insert(view);
-
-    if (firstView) emit layerInAView(layer, true);
-}
-    
-void
-Document::removeFromLayerViewMap(Layer *layer, View *view)
-{
-    if (m_layerViewMap[layer].find(view) ==
-	m_layerViewMap[layer].end()) {
-	std::cerr << "WARNING: Document::removeFromLayerViewMap:"
-		  << " Layer " << layer << " -> view " << view << " not in"
-		  << " layer view map -- internal inconsistency" << std::endl;
-    }
-
-    m_layerViewMap[layer].erase(view);
-
-    if (m_layerViewMap[layer].empty()) {
-        m_layerViewMap.erase(layer);
-        emit layerInAView(layer, false);
-    }
-}
-
-QString
-Document::getUniqueLayerName(QString candidate)
-{
-    for (int count = 1; ; ++count) {
-
-        QString adjusted =
-            (count > 1 ? QString("%1 <%2>").arg(candidate).arg(count) :
-             candidate);
-        
-        bool duplicate = false;
-
-        for (LayerSet::iterator i = m_layers.begin(); i != m_layers.end(); ++i) {
-            if ((*i)->objectName() == adjusted) {
-                duplicate = true;
-                break;
-            }
-        }
-
-        if (!duplicate) return adjusted;
-    }
-}
-
-std::vector<Model *>
-Document::getTransformInputModels()
-{
-    std::vector<Model *> models;
-
-    if (!m_mainModel) return models;
-
-    models.push_back(m_mainModel);
-
-    //!!! This will pick up all models, including those that aren't visible...
-
-    for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
-
-        Model *model = i->first;
-        if (!model || model == m_mainModel) continue;
-        DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
-        
-        if (dtvm) {
-            models.push_back(dtvm);
-        }
-    }
-
-    return models;
-}
-
-void
-Document::alignModel(Model *model)
-{
-    if (!m_mainModel || model == m_mainModel) return;
-
-    RangeSummarisableTimeValueModel *rm = 
-        dynamic_cast<RangeSummarisableTimeValueModel *>(model);
-    if (!rm) return;
-    
-    // This involves creating three new models:
-
-    // 1. an AggregateWaveModel to provide the mixdowns of the main
-    // model and the new model in its two channels, as input to the
-    // MATCH plugin
-
-    // 2. a SparseTimeValueModel, which is the model automatically
-    // created by FeatureExtractionPluginTransform when running the
-    // MATCH plugin (thus containing the alignment path)
-
-    // 3. an AlignmentModel, which stores the path model and carries
-    // out alignment lookups on it.
-
-    // The first two of these are provided as arguments to the
-    // constructor for the third, which takes responsibility for
-    // deleting them.  The AlignmentModel, meanwhile, is passed to the
-    // new model we are aligning, which also takes responsibility for
-    // it.  We should not have to delete any of these new models here.
-
-    AggregateWaveModel::ChannelSpecList components;
-
-    components.push_back(AggregateWaveModel::ModelChannelSpec
-                         (m_mainModel, -1));
-
-    components.push_back(AggregateWaveModel::ModelChannelSpec
-                         (rm, -1));
-
-    Model *aggregate = new AggregateWaveModel(components);
-
-    TransformId id = "vamp:match-vamp-plugin:match:path";
-    
-    TransformFactory *factory = TransformFactory::getInstance();
-
-    Model *transformOutput = factory->transform
-        (id, aggregate,
-         factory->getDefaultContextForTransform(id, aggregate),
-         "<plugin param-serialise=\"1\"/>");
-
-    SparseTimeValueModel *path = dynamic_cast<SparseTimeValueModel *>
-        (transformOutput);
-
-    if (!path) {
-        std::cerr << "Document::alignModel: ERROR: Failed to create alignment path (no MATCH plugin?)" << std::endl;
-        delete transformOutput;
-        delete aggregate;
-        return;
-    }
-
-    AlignmentModel *alignmentModel = new AlignmentModel
-        (m_mainModel, model, aggregate, path);
-
-    rm->setAlignment(alignmentModel);
-}
-
-void
-Document::alignModels()
-{
-    for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
-        alignModel(i->first);
-    }
-}
-
-Document::AddLayerCommand::AddLayerCommand(Document *d,
-					   View *view,
-					   Layer *layer) :
-    m_d(d),
-    m_view(view),
-    m_layer(layer),
-    m_name(qApp->translate("AddLayerCommand", "Add %1 Layer").arg(layer->objectName())),
-    m_added(false)
-{
-}
-
-Document::AddLayerCommand::~AddLayerCommand()
-{
-//    std::cerr << "Document::AddLayerCommand::~AddLayerCommand" << std::endl;
-    if (!m_added) {
-	m_d->deleteLayer(m_layer);
-    }
-}
-
-void
-Document::AddLayerCommand::execute()
-{
-    for (int i = 0; i < m_view->getLayerCount(); ++i) {
-	if (m_view->getLayer(i) == m_layer) {
-	    // already there
-	    m_layer->setLayerDormant(m_view, false);
-	    m_added = true;
-	    return;
-	}
-    }
-
-    m_view->addLayer(m_layer);
-    m_layer->setLayerDormant(m_view, false);
-
-    m_d->addToLayerViewMap(m_layer, m_view);
-    m_added = true;
-}
-
-void
-Document::AddLayerCommand::unexecute()
-{
-    m_view->removeLayer(m_layer);
-    m_layer->setLayerDormant(m_view, true);
-
-    m_d->removeFromLayerViewMap(m_layer, m_view);
-    m_added = false;
-}
-
-Document::RemoveLayerCommand::RemoveLayerCommand(Document *d,
-						 View *view,
-						 Layer *layer) :
-    m_d(d),
-    m_view(view),
-    m_layer(layer),
-    m_name(qApp->translate("RemoveLayerCommand", "Delete %1 Layer").arg(layer->objectName())),
-    m_added(true)
-{
-}
-
-Document::RemoveLayerCommand::~RemoveLayerCommand()
-{
-//    std::cerr << "Document::RemoveLayerCommand::~RemoveLayerCommand" << std::endl;
-    if (!m_added) {
-	m_d->deleteLayer(m_layer);
-    }
-}
-
-void
-Document::RemoveLayerCommand::execute()
-{
-    bool have = false;
-    for (int i = 0; i < m_view->getLayerCount(); ++i) {
-	if (m_view->getLayer(i) == m_layer) {
-	    have = true;
-	    break;
-	}
-    }
-
-    if (!have) { // not there!
-	m_layer->setLayerDormant(m_view, true);
-	m_added = false;
-	return;
-    }
-
-    m_view->removeLayer(m_layer);
-    m_layer->setLayerDormant(m_view, true);
-
-    m_d->removeFromLayerViewMap(m_layer, m_view);
-    m_added = false;
-}
-
-void
-Document::RemoveLayerCommand::unexecute()
-{
-    m_view->addLayer(m_layer);
-    m_layer->setLayerDormant(m_view, false);
-
-    m_d->addToLayerViewMap(m_layer, m_view);
-    m_added = true;
-}
-
-void
-Document::toXml(QTextStream &out, QString indent, QString extraAttributes) const
-{
-    out << indent + QString("<data%1%2>\n")
-        .arg(extraAttributes == "" ? "" : " ").arg(extraAttributes);
-
-    if (m_mainModel) {
-	m_mainModel->toXml(out, indent + "  ", "mainModel=\"true\"");
-    }
-
-    // Models that are not used in a layer that is in a view should
-    // not be written.  Get our list of required models first.
-
-    std::set<const Model *> used;
-
-    for (LayerViewMap::const_iterator i = m_layerViewMap.begin();
-         i != m_layerViewMap.end(); ++i) {
-
-        if (i->first && !i->second.empty() && i->first->getModel()) {
-            used.insert(i->first->getModel());
-        }
-    }
-
-    for (ModelMap::const_iterator i = m_models.begin();
-	 i != m_models.end(); ++i) {
-
-        const Model *model = i->first;
-	const ModelRecord &rec = i->second;
-
-        if (used.find(model) == used.end()) continue;
-        
-        // We need an intelligent way to determine which models need
-        // to be streamed (i.e. have been edited, or are small) and
-        // which should not be (i.e. remain as generated by a
-        // transform, and are large).
-        //
-        // At the moment we can get away with deciding not to stream
-        // dense 3d models or writable wave file models, provided they
-        // were generated from a transform, because at the moment there
-        // is no way to edit those model types so it should be safe to
-        // regenerate them.  That won't always work in future though.
-        // It would be particularly nice to be able to ask the user,
-        // as well as making an intelligent guess.
-
-        bool writeModel = true;
-        bool haveDerivation = false;
-
-        if (rec.source && rec.transform != "") {
-            haveDerivation = true;
-        } 
-
-        if (haveDerivation) {
-            if (dynamic_cast<const WritableWaveFileModel *>(model)) {
-                writeModel = false;
-            } else if (dynamic_cast<const DenseThreeDimensionalModel *>(model)) {
-                writeModel = false;
-            }
-        }
-
-        if (writeModel) {
-            i->first->toXml(out, indent + "  ");
-        }
-
-	if (haveDerivation) {
-
-            QString extentsAttributes;
-            if (rec.context.startFrame != 0 ||
-                rec.context.duration != 0) {
-                extentsAttributes = QString("startFrame=\"%1\" duration=\"%2\" ")
-                    .arg(rec.context.startFrame)
-                    .arg(rec.context.duration);
-            }
-	    
-	    out << indent;
-	    out << QString("  <derivation source=\"%1\" model=\"%2\" channel=\"%3\" domain=\"%4\" stepSize=\"%5\" blockSize=\"%6\" %7windowType=\"%8\" transform=\"%9\"")
-		.arg(XmlExportable::getObjectExportId(rec.source))
-		.arg(XmlExportable::getObjectExportId(i->first))
-                .arg(rec.context.channel)
-                .arg(rec.context.domain)
-                .arg(rec.context.stepSize)
-                .arg(rec.context.blockSize)
-                .arg(extentsAttributes)
-                .arg(int(rec.context.windowType))
-		.arg(XmlExportable::encodeEntities(rec.transform));
-
-            if (rec.configurationXml != "") {
-                out << ">\n    " + indent + rec.configurationXml
-                    + "\n" + indent + "  </derivation>\n";
-            } else {
-                out << "/>\n";
-            }
-	}
-
-        //!!! We should probably own the PlayParameterRepository
-        PlayParameters *playParameters =
-            PlayParameterRepository::getInstance()->getPlayParameters(i->first);
-        if (playParameters) {
-            playParameters->toXml
-                (out, indent + "  ",
-                 QString("model=\"%1\"")
-                 .arg(XmlExportable::getObjectExportId(i->first)));
-        }
-    }
-	    
-    for (LayerSet::const_iterator i = m_layers.begin();
-	 i != m_layers.end(); ++i) {
-
-	(*i)->toXml(out, indent + "  ");
-    }
-
-    out << indent + "</data>\n";
-}
-
-
--- a/document/Document.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,316 +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 _DOCUMENT_H_
-#define _DOCUMENT_H_
-
-#include "layer/LayerFactory.h"
-#include "transform/Transform.h"
-#include "transform/PluginTransform.h"
-#include "base/Command.h"
-
-#include <map>
-#include <set>
-
-class Model;
-class Layer;
-class View;
-class WaveFileModel;
-
-/**
- * A Sonic Visualiser document consists of a set of data models, and
- * also the visualisation layers used to display them.  Changes to the
- * layers and their layout need to be stored and managed in much the
- * same way as changes to the underlying data.
- * 
- * The document manages:
- * 
- *  - A main data Model, which provides the underlying sample rate and
- * such like.  This must be a WaveFileModel.
- * 
- *  - Any number of imported Model objects, which contain data without any
- * requirement to remember where the data came from or how to
- * regenerate it.
- * 
- *  - Any number of Model objects that were generated by a Transform
- * such as FeatureExtractionPluginTransform.  For these, we also
- * record the source model and the name of the transform used to
- * generate the model so that we can regenerate it (potentially
- * from a different source) on demand.
- *
- *  - A flat list of Layer objects.  Elsewhere, the GUI may distribute these
- * across any number of View widgets.  A layer may be viewable on more
- * than one view at once, in principle.  A layer refers to one model,
- * but the same model can be in use in more than one layer.
- *
- * The document does *not* manage the existence or structure of Pane
- * and other view widgets.  However, it does provide convenience
- * methods for reference-counted command-based management of the
- * association between layers and views (addLayerToView,
- * removeLayerFromView).
- */
-
-class Document : public QObject,
-		 public XmlExportable
-{
-    Q_OBJECT
-
-public:
-    Document();
-    virtual ~Document();
-
-    /**
-     * Create and return a new layer of the given type, associated
-     * with no model.  The caller may set any model on this layer, but
-     * the model must also be registered with the document via the
-     * add-model methods below.
-     */
-    Layer *createLayer(LayerFactory::LayerType);
-
-    /**
-     * Create and return a new layer of the given type, associated
-     * with the current main model (if appropriate to the layer type).
-     */
-    Layer *createMainModelLayer(LayerFactory::LayerType);
-
-    /**
-     * Create and return a new layer associated with the given model,
-     * and register the model as an imported model.
-     */
-    Layer *createImportedLayer(Model *);
-
-    /**
-     * Create and return a new layer of the given type, with an
-     * appropriate empty model.  If the given type is not one for
-     * which an empty model can meaningfully be created, return 0.
-     */
-    Layer *createEmptyLayer(LayerFactory::LayerType);
-
-    /**
-     * Create and return a new layer of the given type, associated
-     * with the given transform name.  This method does not run the
-     * transform itself, nor create a model.  The caller can safely
-     * add a model to the layer later, but note that all models used
-     * by a transform layer _must_ be registered with the document
-     * using addDerivedModel below.
-     */
-    Layer *createDerivedLayer(LayerFactory::LayerType, TransformId);
-
-    /**
-     * Create and return a suitable layer for the given transform,
-     * running the transform and associating the resulting model with
-     * the new layer.
-     */
-    Layer *createDerivedLayer(TransformId,
-                              Model *inputModel, 
-                              const PluginTransform::ExecutionContext &context,
-                              QString configurationXml);
-
-    /**
-     * Set the main model (the source for playback sample rate, etc)
-     * to the given wave file model.  This will regenerate any derived
-     * models that were based on the previous main model.
-     */
-    void setMainModel(WaveFileModel *);
-
-    /**
-     * Get the main model (the source for playback sample rate, etc).
-     */
-    WaveFileModel *getMainModel() { return m_mainModel; }
-
-    /**
-     * Get the main model (the source for playback sample rate, etc).
-     */
-    const WaveFileModel *getMainModel() const { return m_mainModel; }
-
-    std::vector<Model *> getTransformInputModels();
-
-    /**
-     * Add a derived model associated with the given transform,
-     * running the transform and returning the resulting model.
-     */
-    Model *addDerivedModel(TransformId transform,
-                           Model *inputModel,
-                           const PluginTransform::ExecutionContext &context,
-                           QString configurationXml);
-
-    /**
-     * Add a derived model associated with the given transform.  This
-     * is necessary to register any derived model that was not created
-     * by the document using createDerivedModel or createDerivedLayer.
-     */
-    void addDerivedModel(TransformId,
-                         Model *inputModel,
-                         const PluginTransform::ExecutionContext &context,
-                         Model *outputModelToAdd,
-                         QString configurationXml);
-
-    /**
-     * Add an imported (non-derived, non-main) model.  This is
-     * necessary to register any imported model that is associated
-     * with a layer.
-     */
-    void addImportedModel(Model *);
-
-    /**
-     * Associate the given model with the given layer.  The model must
-     * have already been registered using one of the addXXModel
-     * methods above.
-     */
-    void setModel(Layer *, Model *);
-
-    /**
-     * Set the given layer to use the given channel of its model (-1
-     * means all available channels).
-     */
-    void setChannel(Layer *, int);
-
-    /**
-     * Add the given layer to the given view.  If the layer is
-     * intended to show a particular model, the model should normally
-     * be set using setModel before this method is called.
-     */
-    void addLayerToView(View *, Layer *);
-
-    /**
-     * Remove the given layer from the given view.
-     */
-    void removeLayerFromView(View *, Layer *);
-
-    void toXml(QTextStream &, QString indent, QString extraAttributes) const;
-signals:
-    void layerAdded(Layer *);
-    void layerRemoved(Layer *);
-    void layerAboutToBeDeleted(Layer *);
-
-    // Emitted when a layer is first added to a view, or when it is
-    // last removed from a view
-    void layerInAView(Layer *, bool);
-
-    void modelAdded(Model *);
-    void mainModelChanged(WaveFileModel *); // emitted after modelAdded
-    void modelAboutToBeDeleted(Model *);
-
-    void modelGenerationFailed(QString transformName);
-    void modelRegenerationFailed(QString layerName, QString transformName);
-
-protected:
-    void releaseModel(Model *model);
-
-    /**
-     * Delete the given layer, and also its associated model if no
-     * longer used by any other layer.  In general, this should be the
-     * only method used to delete layers -- doing so directly is a bit
-     * of a social gaffe.
-     */
-    void deleteLayer(Layer *, bool force = false);
-
-    /**
-     * If model is suitable for alignment, align it against the main
-     * model and store the alignment in the model.
-     */
-    void alignModel(Model *);
-
-    /**
-     * Realign all models if the main model has changed.  Is this wise?
-     */
-    void alignModels();
-
-    /*
-     * Every model that is in use by a layer in the document must be
-     * found in either m_mainModel or m_models.  We own and control
-     * the lifespan of all of these models.
-     */
-
-    /**
-     * The model that provides the underlying sample rate, etc.  This
-     * model is not reference counted for layers, and is not freed
-     * unless it is replaced or the document is deleted.
-     */
-    WaveFileModel *m_mainModel;
-
-    struct ModelRecord
-    {
-	// Information associated with a non-main model.  If this
-	// model is derived from another, then source will be non-NULL
-	// and the transform name will be set appropriately.  If the
-	// transform name is set but source is NULL, then there was a
-	// transform involved but the (target) model has been modified
-	// since being generated from it.
-	const Model *source;
-	TransformId transform;
-        PluginTransform::ExecutionContext context;
-        QString configurationXml;
-
-	// Count of the number of layers using this model.
-	int refcount;
-    };
-
-    typedef std::map<Model *, ModelRecord> ModelMap;
-    ModelMap m_models;
-
-    class AddLayerCommand : public Command
-    {
-    public:
-	AddLayerCommand(Document *d, View *view, Layer *layer);
-	virtual ~AddLayerCommand();
-	
-	virtual void execute();
-	virtual void unexecute();
-	virtual QString getName() const { return m_name; }
-
-    protected:
-	Document *m_d;
-	View *m_view; // I don't own this
-	Layer *m_layer; // Document owns this, but I determine its lifespans
-	QString m_name;
-	bool m_added;
-    };
-
-    class RemoveLayerCommand : public Command
-    {
-    public:
-	RemoveLayerCommand(Document *d, View *view, Layer *layer);
-	virtual ~RemoveLayerCommand();
-	
-	virtual void execute();
-	virtual void unexecute();
-	virtual QString getName() const { return m_name; }
-
-    protected:
-	Document *m_d;
-	View *m_view; // I don't own this
-	Layer *m_layer; // Document owns this, but I determine its lifespan
-	QString m_name;
-	bool m_added;
-    };
-
-    typedef std::map<Layer *, std::set<View *> > LayerViewMap;
-    LayerViewMap m_layerViewMap;
-
-    void addToLayerViewMap(Layer *, View *);
-    void removeFromLayerViewMap(Layer *, View *);
-
-    QString getUniqueLayerName(QString candidate);
-    
-    /**
-     * And these are the layers.  We also control the lifespans of
-     * these (usually through the commands used to add and remove them).
-     */
-    typedef std::set<Layer *> LayerSet;
-    LayerSet m_layers;
-};
-
-#endif
--- a/document/SVFileReader.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1167 +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 "SVFileReader.h"
-
-#include "layer/Layer.h"
-#include "view/View.h"
-#include "base/PlayParameters.h"
-#include "base/PlayParameterRepository.h"
-
-#include "data/fileio/AudioFileReaderFactory.h"
-#include "data/fileio/FileFinder.h"
-#include "data/fileio/FileSource.h"
-
-#include "data/model/WaveFileModel.h"
-#include "data/model/EditableDenseThreeDimensionalModel.h"
-#include "data/model/SparseOneDimensionalModel.h"
-#include "data/model/SparseTimeValueModel.h"
-#include "data/model/NoteModel.h"
-#include "data/model/TextModel.h"
-#include "data/model/ImageModel.h"
-
-#include "view/Pane.h"
-
-#include "Document.h"
-
-#include <QString>
-#include <QMessageBox>
-#include <QFileDialog>
-
-#include <iostream>
-
-SVFileReader::SVFileReader(Document *document,
-			   SVFileReaderPaneCallback &callback,
-                           QString location) :
-    m_document(document),
-    m_paneCallback(callback),
-    m_location(location),
-    m_currentPane(0),
-    m_currentDataset(0),
-    m_currentDerivedModel(0),
-    m_currentDerivedModelId(-1),
-    m_currentPlayParameters(0),
-    m_datasetSeparator(" "),
-    m_inRow(false),
-    m_inLayer(false),
-    m_inView(false),
-    m_rowNumber(0),
-    m_ok(false)
-{
-}
-
-void
-SVFileReader::parse(const QString &xmlData)
-{
-    QXmlInputSource inputSource;
-    inputSource.setData(xmlData);
-    parse(inputSource);
-}
-
-void
-SVFileReader::parse(QXmlInputSource &inputSource)
-{
-    QXmlSimpleReader reader;
-    reader.setContentHandler(this);
-    reader.setErrorHandler(this);
-    m_ok = reader.parse(inputSource);
-}    
-
-bool
-SVFileReader::isOK()
-{
-    return m_ok;
-}
-	
-SVFileReader::~SVFileReader()
-{
-    if (!m_awaitingDatasets.empty()) {
-	std::cerr << "WARNING: SV-XML: File ended with "
-		  << m_awaitingDatasets.size() << " unfilled model dataset(s)"
-		  << std::endl;
-    }
-
-    std::set<Model *> unaddedModels;
-
-    for (std::map<int, Model *>::iterator i = m_models.begin();
-	 i != m_models.end(); ++i) {
-	if (m_addedModels.find(i->second) == m_addedModels.end()) {
-	    unaddedModels.insert(i->second);
-	}
-    }
-
-    if (!unaddedModels.empty()) {
-	std::cerr << "WARNING: SV-XML: File contained "
-		  << unaddedModels.size() << " unused models"
-		  << std::endl;
-	while (!unaddedModels.empty()) {
-	    delete *unaddedModels.begin();
-	    unaddedModels.erase(unaddedModels.begin());
-	}
-    }	
-}
-
-bool
-SVFileReader::startElement(const QString &, const QString &,
-			   const QString &qName,
-			   const QXmlAttributes &attributes)
-{
-    QString name = qName.toLower();
-
-    bool ok = false;
-
-    // Valid element names:
-    //
-    // sv
-    // data
-    // dataset
-    // display
-    // derivation
-    // playparameters
-    // layer
-    // model
-    // point
-    // row
-    // view
-    // window
-
-    if (name == "sv") {
-
-	// nothing needed
-	ok = true;
-
-    } else if (name == "data") {
-
-	// nothing needed
-	m_inData = true;
-	ok = true;
-
-    } else if (name == "display") {
-
-	// nothing needed
-	ok = true;
-
-    } else if (name == "window") {
-
-	ok = readWindow(attributes);
-
-    } else if (name == "model") {
-
-	ok = readModel(attributes);
-    
-    } else if (name == "dataset") {
-	
-	ok = readDatasetStart(attributes);
-
-    } else if (name == "bin") {
-	
-	ok = addBinToDataset(attributes);
-    
-    } else if (name == "point") {
-	
-	ok = addPointToDataset(attributes);
-
-    } else if (name == "row") {
-
-	ok = addRowToDataset(attributes);
-
-    } else if (name == "layer") {
-
-        addUnaddedModels(); // all models must be specified before first layer
-	ok = readLayer(attributes);
-
-    } else if (name == "view") {
-
-	m_inView = true;
-	ok = readView(attributes);
-
-    } else if (name == "derivation") {
-
-	ok = readDerivation(attributes);
-
-    } else if (name == "playparameters") {
-        
-        ok = readPlayParameters(attributes);
-
-    } else if (name == "plugin") {
-
-	ok = readPlugin(attributes);
-
-    } else if (name == "selections") {
-
-	m_inSelections = true;
-	ok = true;
-
-    } else if (name == "selection") {
-
-	ok = readSelection(attributes);
-
-    } else if (name == "measurement") {
-
-        ok = readMeasurement(attributes);
-
-    } else {
-        std::cerr << "WARNING: SV-XML: Unexpected element \""
-                  << name.toLocal8Bit().data() << "\"" << std::endl;
-    }
-
-    if (!ok) {
-	std::cerr << "WARNING: SV-XML: Failed to completely process element \""
-		  << name.toLocal8Bit().data() << "\"" << std::endl;
-    }
-
-    return true;
-}
-
-bool
-SVFileReader::characters(const QString &text)
-{
-    bool ok = false;
-
-    if (m_inRow) {
-	ok = readRowData(text);
-	if (!ok) {
-	    std::cerr << "WARNING: SV-XML: Failed to read row data content for row " << m_rowNumber << std::endl;
-	}
-    }
-
-    return true;
-}
-
-bool
-SVFileReader::endElement(const QString &, const QString &,
-			 const QString &qName)
-{
-    QString name = qName.toLower();
-
-    if (name == "dataset") {
-
-	if (m_currentDataset) {
-	    
-	    bool foundInAwaiting = false;
-
-	    for (std::map<int, int>::iterator i = m_awaitingDatasets.begin();
-		 i != m_awaitingDatasets.end(); ++i) {
-		if (haveModel(i->second) &&
-                    m_models[i->second] == m_currentDataset) {
-		    m_awaitingDatasets.erase(i);
-		    foundInAwaiting = true;
-		    break;
-		}
-	    }
-
-	    if (!foundInAwaiting) {
-		std::cerr << "WARNING: SV-XML: Dataset precedes model, or no model uses dataset" << std::endl;
-	    }
-	}
-
-	m_currentDataset = 0;
-
-    } else if (name == "data") {
-
-        addUnaddedModels();
-	m_inData = false;
-
-    } else if (name == "derivation") {
-
-        if (!m_currentDerivedModel) {
-            if (m_currentDerivedModel < 0) {
-                std::cerr << "WARNING: SV-XML: Bad derivation output model id "
-                          << m_currentDerivedModelId << std::endl;
-            } else if (haveModel(m_currentDerivedModelId)) {
-                std::cerr << "WARNING: SV-XML: Derivation has existing model "
-                          << m_currentDerivedModelId
-                          << " as target, not regenerating" << std::endl;
-            } else {
-                m_currentDerivedModel = m_models[m_currentDerivedModelId] =
-                    m_document->addDerivedModel(m_currentTransform,
-                                                m_currentTransformSource,
-                                                m_currentTransformContext,
-                                                m_currentTransformConfiguration);
-            }
-        } else {
-            m_document->addDerivedModel(m_currentTransform,
-                                        m_currentTransformSource,
-                                        m_currentTransformContext,
-                                        m_currentDerivedModel,
-                                        m_currentTransformConfiguration);
-        }
-
-        m_addedModels.insert(m_currentDerivedModel);
-        m_currentDerivedModel = 0;
-        m_currentDerivedModelId = -1;
-        m_currentTransform = "";
-        m_currentTransformConfiguration = "";
-
-    } else if (name == "row") {
-	m_inRow = false;
-    } else if (name == "layer") {
-        m_inLayer = false;
-    } else if (name == "view") {
-	m_inView = false;
-    } else if (name == "selections") {
-	m_inSelections = false;
-    } else if (name == "playparameters") {
-        m_currentPlayParameters = 0;
-    }
-
-    return true;
-}
-
-bool
-SVFileReader::error(const QXmlParseException &exception)
-{
-    m_errorString =
-	QString("ERROR: SV-XML: %1 at line %2, column %3")
-	.arg(exception.message())
-	.arg(exception.lineNumber())
-	.arg(exception.columnNumber());
-    std::cerr << m_errorString.toLocal8Bit().data() << std::endl;
-    return QXmlDefaultHandler::error(exception);
-}
-
-bool
-SVFileReader::fatalError(const QXmlParseException &exception)
-{
-    m_errorString =
-	QString("FATAL ERROR: SV-XML: %1 at line %2, column %3")
-	.arg(exception.message())
-	.arg(exception.lineNumber())
-	.arg(exception.columnNumber());
-    std::cerr << m_errorString.toLocal8Bit().data() << std::endl;
-    return QXmlDefaultHandler::fatalError(exception);
-}
-
-
-#define READ_MANDATORY(TYPE, NAME, CONVERSION)		      \
-    TYPE NAME = attributes.value(#NAME).trimmed().CONVERSION(&ok); \
-    if (!ok) { \
-	std::cerr << "WARNING: SV-XML: Missing or invalid mandatory " #TYPE " attribute \"" #NAME "\"" << std::endl; \
-	return false; \
-    }
-
-bool
-SVFileReader::readWindow(const QXmlAttributes &attributes)
-{
-    bool ok = false;
-
-    READ_MANDATORY(int, width, toInt);
-    READ_MANDATORY(int, height, toInt);
-
-    m_paneCallback.setWindowSize(width, height);
-    return true;
-}
-
-void
-SVFileReader::addUnaddedModels()
-{
-    std::set<Model *> unaddedModels;
-    
-    for (std::map<int, Model *>::iterator i = m_models.begin();
-         i != m_models.end(); ++i) {
-        if (m_addedModels.find(i->second) == m_addedModels.end()) {
-            unaddedModels.insert(i->second);
-        }
-    }
-    
-    for (std::set<Model *>::iterator i = unaddedModels.begin();
-         i != unaddedModels.end(); ++i) {
-        m_document->addImportedModel(*i);
-        m_addedModels.insert(*i);
-    }
-}
-
-bool
-SVFileReader::readModel(const QXmlAttributes &attributes)
-{
-    bool ok = false;
-
-    READ_MANDATORY(int, id, toInt);
-
-    if (haveModel(id)) {
-	std::cerr << "WARNING: SV-XML: Ignoring duplicate model id " << id
-		  << std::endl;
-	return false;
-    }
-
-    QString name = attributes.value("name");
-
-    std::cerr << "SVFileReader::readModel: model name \"" << name.toStdString() << "\"" << std::endl;
-
-    READ_MANDATORY(int, sampleRate, toInt);
-
-    QString type = attributes.value("type").trimmed();
-    bool mainModel = (attributes.value("mainModel").trimmed() == "true");
-
-    if (type == "wavefile") {
-	
-        WaveFileModel *model = 0;
-        FileFinder *ff = FileFinder::getInstance();
-        QString originalPath = attributes.value("file");
-        QString path = ff->find(FileFinder::AudioFile,
-                                originalPath, m_location);
-
-        FileSource file(path);
-        file.waitForStatus();
-
-        if (!file.isOK()) {
-            std::cerr << "SVFileReader::readModel: Failed to retrieve file \"" << path.toStdString() << "\" for wave file model: " << file.getErrorString().toStdString() << std::endl;
-        } else if (!file.isAvailable()) {
-            std::cerr << "SVFileReader::readModel: Failed to retrieve file \"" << path.toStdString() << "\" for wave file model: Source unavailable" << std::endl;
-        } else {
-
-            file.waitForData();
-            model = new WaveFileModel(file);
-            if (!model->isOK()) {
-                delete model;
-                model = 0;
-            }
-        }
-
-        if (!model) return false;
-
-        model->setObjectName(name);
-	m_models[id] = model;
-	if (mainModel) {
-	    m_document->setMainModel(model);
-	    m_addedModels.insert(model);
-	}
-	// Derived models will be added when their derivation
-	// is found.
-
-	return true;
-
-    } else if (type == "dense") {
-	
-	READ_MANDATORY(int, dimensions, toInt);
-		    
-	// Currently the only dense model we support here is the dense
-	// 3d model.  Dense time-value models are always file-backed
-	// waveform data, at this point, and they come in as wavefile
-	// models.
-	
-	if (dimensions == 3) {
-	    
-	    READ_MANDATORY(int, windowSize, toInt);
-	    READ_MANDATORY(int, yBinCount, toInt);
-	    
-            EditableDenseThreeDimensionalModel *model =
-		new EditableDenseThreeDimensionalModel
-                (sampleRate, windowSize, yBinCount);
-	    
-	    float minimum = attributes.value("minimum").trimmed().toFloat(&ok);
-	    if (ok) model->setMinimumLevel(minimum);
-	    
-	    float maximum = attributes.value("maximum").trimmed().toFloat(&ok);
-	    if (ok) model->setMaximumLevel(maximum);
-
-	    int dataset = attributes.value("dataset").trimmed().toInt(&ok);
-	    if (ok) m_awaitingDatasets[dataset] = id;
-
-            model->setObjectName(name);
-	    m_models[id] = model;
-	    return true;
-
-	} else {
-
-	    std::cerr << "WARNING: SV-XML: Unexpected dense model dimension ("
-		      << dimensions << ")" << std::endl;
-	}
-    } else if (type == "sparse") {
-
-	READ_MANDATORY(int, dimensions, toInt);
-		  
-	if (dimensions == 1) {
-	    
-	    READ_MANDATORY(int, resolution, toInt);
-
-            if (attributes.value("subtype") == "image") {
-
-                bool notifyOnAdd = (attributes.value("notifyOnAdd") == "true");
-                ImageModel *model = new ImageModel(sampleRate, resolution,
-                                                   notifyOnAdd);
-                model->setObjectName(name);
-                m_models[id] = model;
-
-            } else {
-
-                SparseOneDimensionalModel *model = new SparseOneDimensionalModel
-                    (sampleRate, resolution);
-                model->setObjectName(name);
-                m_models[id] = model;
-            }
-
-	    int dataset = attributes.value("dataset").trimmed().toInt(&ok);
-	    if (ok) m_awaitingDatasets[dataset] = id;
-
-	    return true;
-
-	} else if (dimensions == 2 || dimensions == 3) {
-	    
-	    READ_MANDATORY(int, resolution, toInt);
-
-            bool haveMinMax = true;
-	    float minimum = attributes.value("minimum").trimmed().toFloat(&ok);
-            if (!ok) haveMinMax = false;
-	    float maximum = attributes.value("maximum").trimmed().toFloat(&ok);
-            if (!ok) haveMinMax = false;
-
-	    float valueQuantization =
-		attributes.value("valueQuantization").trimmed().toFloat(&ok);
-
-	    bool notifyOnAdd = (attributes.value("notifyOnAdd") == "true");
-
-            QString units = attributes.value("units");
-
-	    if (dimensions == 2) {
-		if (attributes.value("subtype") == "text") {
-		    TextModel *model = new TextModel
-			(sampleRate, resolution, notifyOnAdd);
-                    model->setObjectName(name);
-		    m_models[id] = model;
-		} else {
-		    SparseTimeValueModel *model;
-                    if (haveMinMax) {
-                        model = new SparseTimeValueModel
-                            (sampleRate, resolution, minimum, maximum, notifyOnAdd);
-                    } else {
-                        model = new SparseTimeValueModel
-                            (sampleRate, resolution, notifyOnAdd);
-                    }
-                    model->setScaleUnits(units);
-                    model->setObjectName(name);
-		    m_models[id] = model;
-		}
-	    } else {
-		NoteModel *model;
-                if (haveMinMax) {
-                    model = new NoteModel
-                        (sampleRate, resolution, minimum, maximum, notifyOnAdd);
-                } else {
-                    model = new NoteModel
-                        (sampleRate, resolution, notifyOnAdd);
-                }
-		model->setValueQuantization(valueQuantization);
-                model->setScaleUnits(units);
-                model->setObjectName(name);
-		m_models[id] = model;
-	    }
-
-	    int dataset = attributes.value("dataset").trimmed().toInt(&ok);
-	    if (ok) m_awaitingDatasets[dataset] = id;
-
-	    return true;
-
-	} else {
-
-	    std::cerr << "WARNING: SV-XML: Unexpected sparse model dimension ("
-		      << dimensions << ")" << std::endl;
-	}
-    } else {
-
-	std::cerr << "WARNING: SV-XML: Unexpected model type \""
-		  << type.toLocal8Bit().data() << "\" for model id " << id << std::endl;
-    }
-
-    return false;
-}
-
-bool
-SVFileReader::readView(const QXmlAttributes &attributes)
-{
-    QString type = attributes.value("type");
-    m_currentPane = 0;
-    
-    if (type != "pane") {
-	std::cerr << "WARNING: SV-XML: Unexpected view type \""
-		  << type.toLocal8Bit().data() << "\"" << std::endl;
-	return false;
-    }
-
-    m_currentPane = m_paneCallback.addPane();
-
-    if (!m_currentPane) {
-	std::cerr << "WARNING: SV-XML: Internal error: Failed to add pane!"
-		  << std::endl;
-	return false;
-    }
-
-    bool ok = false;
-
-    View *view = m_currentPane;
-
-    // The view properties first
-
-    READ_MANDATORY(size_t, centre, toUInt);
-    READ_MANDATORY(size_t, zoom, toUInt);
-    READ_MANDATORY(int, followPan, toInt);
-    READ_MANDATORY(int, followZoom, toInt);
-    QString tracking = attributes.value("tracking");
-
-    // Specify the follow modes before we set the actual values
-    view->setFollowGlobalPan(followPan);
-    view->setFollowGlobalZoom(followZoom);
-    view->setPlaybackFollow(tracking == "scroll" ? PlaybackScrollContinuous :
-			    tracking == "page" ? PlaybackScrollPage
-			    : PlaybackIgnore);
-
-    // Then set these values
-    view->setCentreFrame(centre);
-    view->setZoomLevel(zoom);
-
-    // And pane properties
-    READ_MANDATORY(int, centreLineVisible, toInt);
-    m_currentPane->setCentreLineVisible(centreLineVisible);
-
-    int height = attributes.value("height").toInt(&ok);
-    if (ok) {
-	m_currentPane->resize(m_currentPane->width(), height);
-    }
-
-    return true;
-}
-
-bool
-SVFileReader::readLayer(const QXmlAttributes &attributes)
-{
-    QString type = attributes.value("type");
-
-    int id;
-    bool ok = false;
-    id = attributes.value("id").trimmed().toInt(&ok);
-
-    if (!ok) {
-	std::cerr << "WARNING: SV-XML: No layer id for layer of type \""
-		  << type.toLocal8Bit().data()
-		  << "\"" << std::endl;
-	return false;
-    }
-
-    Layer *layer = 0;
-    bool isNewLayer = false;
-
-    // Layers are expected to be defined in layer elements in the data
-    // section, and referred to in layer elements in the view
-    // sections.  So if we're in the data section, we expect this
-    // layer not to exist already; if we're in the view section, we
-    // expect it to exist.
-
-    if (m_inData) {
-
-	if (m_layers.find(id) != m_layers.end()) {
-	    std::cerr << "WARNING: SV-XML: Ignoring duplicate layer id " << id
-		      << " in data section" << std::endl;
-	    return false;
-	}
-
-	layer = m_layers[id] = m_document->createLayer
-	    (LayerFactory::getInstance()->getLayerTypeForName(type));
-
-	if (layer) {
-	    m_layers[id] = layer;
-	    isNewLayer = true;
-	}
-
-    } else {
-
-	if (!m_currentPane) {
-	    std::cerr << "WARNING: SV-XML: No current pane for layer " << id
-		      << " in view section" << std::endl;
-	    return false;
-	}
-
-	if (m_layers.find(id) != m_layers.end()) {
-	    
-	    layer = m_layers[id];
-	
-	} else {
-	    std::cerr << "WARNING: SV-XML: Layer id " << id 
-		      << " in view section has not been defined -- defining it here"
-		      << std::endl;
-
-	    layer = m_document->createLayer
-		(LayerFactory::getInstance()->getLayerTypeForName(type));
-
-	    if (layer) {
-		m_layers[id] = layer;
-		isNewLayer = true;
-	    }
-	}
-    }
-	    
-    if (!layer) {
-	std::cerr << "WARNING: SV-XML: Failed to add layer of type \""
-		  << type.toLocal8Bit().data()
-		  << "\"" << std::endl;
-	return false;
-    }
-
-    if (isNewLayer) {
-
-	QString name = attributes.value("name");
-	layer->setObjectName(name);
-
-	int modelId;
-	bool modelOk = false;
-	modelId = attributes.value("model").trimmed().toInt(&modelOk);
-
-	if (modelOk) {
-	    if (haveModel(modelId)) {
-		Model *model = m_models[modelId];
-		m_document->setModel(layer, model);
-	    } else {
-		std::cerr << "WARNING: SV-XML: Unknown model id " << modelId
-			  << " in layer definition" << std::endl;
-	    }
-	}
-
-	layer->setProperties(attributes);
-    }
-
-    if (!m_inData && m_currentPane) {
-
-        QString visible = attributes.value("visible");
-        bool dormant = (visible == "false");
-
-        // We need to do this both before and after adding the layer
-        // to the view -- we need it to be dormant if appropriate
-        // before it's actually added to the view so that any property
-        // box gets the right state when it's added, but the add layer
-        // command sets dormant to false because it assumes it may be
-        // restoring a previously dormant layer, so we need to set it
-        // again afterwards too.  Hm
-        layer->setLayerDormant(m_currentPane, dormant);
-
-	m_document->addLayerToView(m_currentPane, layer);
-
-        layer->setLayerDormant(m_currentPane, dormant);
-    }
-
-    m_currentLayer = layer;
-    m_inLayer = true;
-
-    return true;
-}
-
-bool
-SVFileReader::readDatasetStart(const QXmlAttributes &attributes)
-{
-    bool ok = false;
-
-    READ_MANDATORY(int, id, toInt);
-    READ_MANDATORY(int, dimensions, toInt);
-    
-    if (m_awaitingDatasets.find(id) == m_awaitingDatasets.end()) {
-	std::cerr << "WARNING: SV-XML: Unwanted dataset " << id << std::endl;
-	return false;
-    }
-    
-    int modelId = m_awaitingDatasets[id];
-    
-    Model *model = 0;
-    if (haveModel(modelId)) {
-	model = m_models[modelId];
-    } else {
-	std::cerr << "WARNING: SV-XML: Internal error: Unknown model " << modelId
-		  << " expecting dataset " << id << std::endl;
-	return false;
-    }
-
-    bool good = false;
-
-    switch (dimensions) {
-    case 1:
-	if (dynamic_cast<SparseOneDimensionalModel *>(model)) good = true;
-        else if (dynamic_cast<ImageModel *>(model)) good = true;
-	break;
-
-    case 2:
-	if (dynamic_cast<SparseTimeValueModel *>(model)) good = true;
-	else if (dynamic_cast<TextModel *>(model)) good = true;
-	break;
-
-    case 3:
-	if (dynamic_cast<NoteModel *>(model)) good = true;
-	else if (dynamic_cast<EditableDenseThreeDimensionalModel *>(model)) {
-	    m_datasetSeparator = attributes.value("separator");
-	    good = true;
-	}
-	break;
-    }
-
-    if (!good) {
-	std::cerr << "WARNING: SV-XML: Model id " << modelId << " has wrong number of dimensions or inappropriate type for " << dimensions << "-D dataset " << id << std::endl;
-	m_currentDataset = 0;
-	return false;
-    }
-
-    m_currentDataset = model;
-    return true;
-}
-
-bool
-SVFileReader::addPointToDataset(const QXmlAttributes &attributes)
-{
-    bool ok = false;
-
-    READ_MANDATORY(int, frame, toInt);
-
-//    std::cerr << "SVFileReader::addPointToDataset: frame = " << frame << std::endl;
-
-    SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
-	(m_currentDataset);
-
-    if (sodm) {
-//        std::cerr << "Current dataset is a sparse one dimensional model" << std::endl;
-	QString label = attributes.value("label");
-	sodm->addPoint(SparseOneDimensionalModel::Point(frame, label));
-	return true;
-    }
-
-    SparseTimeValueModel *stvm = dynamic_cast<SparseTimeValueModel *>
-	(m_currentDataset);
-
-    if (stvm) {
-//        std::cerr << "Current dataset is a sparse time-value model" << std::endl;
-	float value = 0.0;
-	value = attributes.value("value").trimmed().toFloat(&ok);
-	QString label = attributes.value("label");
-	stvm->addPoint(SparseTimeValueModel::Point(frame, value, label));
-	return ok;
-    }
-	
-    NoteModel *nm = dynamic_cast<NoteModel *>(m_currentDataset);
-
-    if (nm) {
-//        std::cerr << "Current dataset is a note model" << std::endl;
-	float value = 0.0;
-	value = attributes.value("value").trimmed().toFloat(&ok);
-	size_t duration = 0;
-	duration = attributes.value("duration").trimmed().toUInt(&ok);
-	QString label = attributes.value("label");
-	nm->addPoint(NoteModel::Point(frame, value, duration, label));
-	return ok;
-    }
-
-    TextModel *tm = dynamic_cast<TextModel *>(m_currentDataset);
-
-    if (tm) {
-//        std::cerr << "Current dataset is a text model" << std::endl;
-	float height = 0.0;
-	height = attributes.value("height").trimmed().toFloat(&ok);
-	QString label = attributes.value("label");
-//        std::cerr << "SVFileReader::addPointToDataset: TextModel: frame = " << frame << ", height = " << height << ", label = " << label.toStdString() << ", ok = " << ok << std::endl;
-	tm->addPoint(TextModel::Point(frame, height, label));
-	return ok;
-    }
-
-    ImageModel *im = dynamic_cast<ImageModel *>(m_currentDataset);
-
-    if (im) {
-//        std::cerr << "Current dataset is an image model" << std::endl;
-	QString image = attributes.value("image");
-	QString label = attributes.value("label");
-//        std::cerr << "SVFileReader::addPointToDataset: ImageModel: frame = " << frame << ", image = " << image.toStdString() << ", label = " << label.toStdString() << ", ok = " << ok << std::endl;
-	im->addPoint(ImageModel::Point(frame, image, label));
-	return ok;
-    }
-
-    std::cerr << "WARNING: SV-XML: Point element found in non-point dataset" << std::endl;
-
-    return false;
-}
-
-bool
-SVFileReader::addBinToDataset(const QXmlAttributes &attributes)
-{
-    EditableDenseThreeDimensionalModel *dtdm = 
-        dynamic_cast<EditableDenseThreeDimensionalModel *>
-	(m_currentDataset);
-
-    if (dtdm) {
-
-	bool ok = false;
-	int n = attributes.value("number").trimmed().toInt(&ok);
-	if (!ok) {
-	    std::cerr << "WARNING: SV-XML: Missing or invalid bin number"
-		      << std::endl;
-	    return false;
-	}
-
-	QString name = attributes.value("name");
-
-	dtdm->setBinName(n, name);
-	return true;
-    }
-
-    std::cerr << "WARNING: SV-XML: Bin definition found in incompatible dataset" << std::endl;
-
-    return false;
-}
-
-
-bool
-SVFileReader::addRowToDataset(const QXmlAttributes &attributes)
-{
-    m_inRow = false;
-
-    bool ok = false;
-    m_rowNumber = attributes.value("n").trimmed().toInt(&ok);
-    if (!ok) {
-	std::cerr << "WARNING: SV-XML: Missing or invalid row number"
-		  << std::endl;
-	return false;
-    }
-    
-    m_inRow = true;
-
-//    std::cerr << "SV-XML: In row " << m_rowNumber << std::endl;
-    
-    return true;
-}
-
-bool
-SVFileReader::readRowData(const QString &text)
-{
-    EditableDenseThreeDimensionalModel *dtdm =
-        dynamic_cast<EditableDenseThreeDimensionalModel *>
-	(m_currentDataset);
-
-    bool warned = false;
-
-    if (dtdm) {
-	QStringList data = text.split(m_datasetSeparator);
-
-	DenseThreeDimensionalModel::Column values;
-
-	for (QStringList::iterator i = data.begin(); i != data.end(); ++i) {
-
-	    if (values.size() == dtdm->getHeight()) {
-		if (!warned) {
-		    std::cerr << "WARNING: SV-XML: Too many y-bins in 3-D dataset row "
-			      << m_rowNumber << std::endl;
-		    warned = true;
-		}
-	    }
-
-	    bool ok;
-	    float value = i->toFloat(&ok);
-	    if (!ok) {
-		std::cerr << "WARNING: SV-XML: Bad floating-point value "
-			  << i->toLocal8Bit().data()
-			  << " in row data" << std::endl;
-	    } else {
-		values.push_back(value);
-	    }
-	}
-
-	dtdm->setColumn(m_rowNumber, values);
-	return true;
-    }
-
-    std::cerr << "WARNING: SV-XML: Row data found in non-row dataset" << std::endl;
-
-    return false;
-}
-
-bool
-SVFileReader::readDerivation(const QXmlAttributes &attributes)
-{
-    int modelId = 0;
-    bool modelOk = false;
-    modelId = attributes.value("model").trimmed().toInt(&modelOk);
-
-    if (!modelOk) {
-	std::cerr << "WARNING: SV-XML: No model id specified for derivation" << std::endl;
-	return false;
-    }
-
-    QString transform = attributes.value("transform");
-
-    if (haveModel(modelId)) {
-        m_currentDerivedModel = m_models[modelId];
-    } else {
-        // we'll regenerate the model when the derivation element ends
-        m_currentDerivedModel = 0;
-    }
-    
-    m_currentDerivedModelId = modelId;
-    
-    int sourceId = 0;
-    bool sourceOk = false;
-    sourceId = attributes.value("source").trimmed().toInt(&sourceOk);
-
-    if (sourceOk && haveModel(sourceId)) {
-        m_currentTransformSource = m_models[sourceId];
-    } else {
-        m_currentTransformSource = m_document->getMainModel();
-    }
-
-    m_currentTransform = transform;
-    m_currentTransformConfiguration = "";
-
-    m_currentTransformContext = PluginTransform::ExecutionContext();
-
-    bool ok = false;
-    int channel = attributes.value("channel").trimmed().toInt(&ok);
-    if (ok) m_currentTransformContext.channel = channel;
-
-    int domain = attributes.value("domain").trimmed().toInt(&ok);
-    if (ok) m_currentTransformContext.domain = Vamp::Plugin::InputDomain(domain);
-
-    int stepSize = attributes.value("stepSize").trimmed().toInt(&ok);
-    if (ok) m_currentTransformContext.stepSize = stepSize;
-
-    int blockSize = attributes.value("blockSize").trimmed().toInt(&ok);
-    if (ok) m_currentTransformContext.blockSize = blockSize;
-
-    int windowType = attributes.value("windowType").trimmed().toInt(&ok);
-    if (ok) m_currentTransformContext.windowType = WindowType(windowType);
-
-    QString startFrameStr = attributes.value("startFrame");
-    QString durationStr = attributes.value("duration");
-
-    size_t startFrame = 0;
-    size_t duration = 0;
-
-    if (startFrameStr != "") {
-        startFrame = startFrameStr.trimmed().toInt(&ok);
-        if (!ok) startFrame = 0;
-    }
-    if (durationStr != "") {
-        duration = durationStr.trimmed().toInt(&ok);
-        if (!ok) duration = 0;
-    }
-
-    m_currentTransformContext.startFrame = startFrame;
-    m_currentTransformContext.duration = duration;
-
-    return true;
-}
-
-bool
-SVFileReader::readPlayParameters(const QXmlAttributes &attributes)
-{
-    m_currentPlayParameters = 0;
-
-    int modelId = 0;
-    bool modelOk = false;
-    modelId = attributes.value("model").trimmed().toInt(&modelOk);
-
-    if (!modelOk) {
-	std::cerr << "WARNING: SV-XML: No model id specified for play parameters" << std::endl;
-	return false;
-    }
-
-    if (haveModel(modelId)) {
-
-        bool ok = false;
-
-        PlayParameters *parameters = PlayParameterRepository::getInstance()->
-            getPlayParameters(m_models[modelId]);
-
-        if (!parameters) {
-            std::cerr << "WARNING: SV-XML: Play parameters for model "
-                      << modelId
-                      << " not found - has model been added to document?"
-                      << std::endl;
-            return false;
-        }
-        
-        bool muted = (attributes.value("mute").trimmed() == "true");
-        parameters->setPlayMuted(muted);
-        
-        float pan = attributes.value("pan").toFloat(&ok);
-        if (ok) parameters->setPlayPan(pan);
-        
-        float gain = attributes.value("gain").toFloat(&ok);
-        if (ok) parameters->setPlayGain(gain);
-        
-        QString pluginId = attributes.value("pluginId");
-        if (pluginId != "") parameters->setPlayPluginId(pluginId);
-        
-        m_currentPlayParameters = parameters;
-
-//        std::cerr << "Current play parameters for model: " << m_models[modelId] << ": " << m_currentPlayParameters << std::endl;
-
-    } else {
-
-	std::cerr << "WARNING: SV-XML: Unknown model " << modelId
-		  << " for play parameters" << std::endl;
-        return false;
-    }
-
-    return true;
-}
-
-bool
-SVFileReader::readPlugin(const QXmlAttributes &attributes)
-{
-    if (m_currentDerivedModelId < 0 && !m_currentPlayParameters) {
-        std::cerr << "WARNING: SV-XML: Plugin found outside derivation or play parameters" << std::endl;
-        return false;
-    }
-
-    QString configurationXml = "<plugin";
-    
-    for (int i = 0; i < attributes.length(); ++i) {
-        configurationXml += QString(" %1=\"%2\"")
-            .arg(attributes.qName(i))
-            .arg(XmlExportable::encodeEntities(attributes.value(i)));
-    }
-
-    configurationXml += "/>";
-
-    if (m_currentPlayParameters) {
-        m_currentPlayParameters->setPlayPluginConfiguration(configurationXml);
-    } else {
-        m_currentTransformConfiguration += configurationXml;
-    }
-
-    return true;
-}
-
-bool
-SVFileReader::readSelection(const QXmlAttributes &attributes)
-{
-    bool ok;
-
-    READ_MANDATORY(int, start, toInt);
-    READ_MANDATORY(int, end, toInt);
-
-    m_paneCallback.addSelection(start, end);
-
-    return true;
-}
-
-bool
-SVFileReader::readMeasurement(const QXmlAttributes &attributes)
-{
-    std::cerr << "SVFileReader::readMeasurement: inLayer "
-              << m_inLayer << ", layer " << m_currentLayer << std::endl;
-
-    if (!m_inLayer) {
-        std::cerr << "WARNING: SV-XML: Measurement found outside layer" << std::endl;
-        return false;
-    }
-
-    m_currentLayer->addMeasurementRect(attributes);
-    return true;
-}
-
-SVFileReaderPaneCallback::~SVFileReaderPaneCallback()
-{
-}
-
--- a/document/SVFileReader.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,234 +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 _SV_FILE_READER_H_
-#define _SV_FILE_READER_H_
-
-#include "layer/LayerFactory.h"
-#include "transform/Transform.h"
-#include "transform/PluginTransform.h"
-
-#include <QXmlDefaultHandler>
-
-#include <map>
-
-class Pane;
-class Model;
-class Document;
-class PlayParameters;
-
-class SVFileReaderPaneCallback
-{
-public:
-    virtual ~SVFileReaderPaneCallback();
-    virtual Pane *addPane() = 0;
-    virtual void setWindowSize(int width, int height) = 0;
-    virtual void addSelection(int start, int end) = 0;
-};
-
-/**
-    SVFileReader loads Sonic Visualiser XML files.  (The SV file
-    format is bzipped XML.)
-
-    Some notes about the SV XML format follow.  We're very lazy with
-    our XML: there's no schema or DTD, and we depend heavily on
-    elements being in a particular order.
- 
-\verbatim
-
-    <sv>
-
-    <data>
-
-      <!-- The data section contains definitions of both models and
-           visual layers.  Layers are considered data in the document;
-           the structure of views that displays the layers is not. -->
-
-      <!-- id numbers are unique within the data type (i.e. no two
-           models can have the same id, but a model can have the same
-           id as a layer, etc).  SV generates its id numbers just for
-           the purpose of cross-referencing within the current file;
-           they don't necessarily have any meaning once the file has
-           been loaded. -->
-
-      <model id="0" name="..." type="..." ... />
-      <model id="1" name="..." type="..." ... />
-
-      <!-- Models that have data associated with them store it
-           in a neighbouring dataset element.  The dataset must follow
-           the model and precede any derivation or layer elements that
-           refer to the model. -->
-
-      <model id="2" name="..." type="..." dataset="0" ... />
-
-      <dataset id="0" type="..."> 
-        <point frame="..." value="..." ... />
-      </dataset>
-
-      <!-- Where one model is derived from another via a transform,
-           it has an associated derivation element.  This must follow
-           both the source and target model elements.  The source and
-           model attributes give the source model id and target model
-           id respectively.  A model can have both dataset and
-           derivation elements; if it does, dataset must appear first. 
-           If the model's data are not stored, but instead the model
-           is to be regenerated completely from the transform when 
-           the session is reloaded, then the model should have _only_
-           a derivation element, and no model element should appear
-           for it at all. -->
-
-      <derivation source="0" model="2" transform="..." ...>
-        <plugin id="..." ... />
-      </derivation>
-
-      <!-- The playparameters element lists playback settings for
-           a model. -->
-
-      <playparameters mute="false" pan="0" gain="1" model="1" ... />
-
-      <!-- Layer elements.  The models must have already been defined.
-           The same model may appear in more than one layer (of more
-           than one type). -->
-
-      <layer id="1" type="..." name="..." model="0" ... />
-      <layer id="2" type="..." name="..." model="1" ... />
-
-    </data>
-
-
-    <display>
-
-      <!-- The display element contains visual structure for the
-           layers.  It's simpler than the data section. -->
-
-      <!-- Overall preferred window size for this session. -->
-
-      <window width="..." height="..."/>
-
-      <!-- List of view elements to stack up.  Each one contains
-           a list of layers in stacking order, back to front. -->
-
-      <view type="pane" ...>
-        <layer id="1"/>
-        <layer id="2"/>
-      </view>
-
-      <!-- The layer elements just refer to layers defined in the
-           data section, so they don't have to have any attributes
-           other than the id.  For sort-of-historical reasons SV
-           actually does repeat the other attributes here, but
-           it doesn't need to. -->
-
-      <view type="pane" ...>
-        <layer id="2"/>
-      <view>
-
-    </display>
-
-
-    <!-- List of selected regions by audio frame extents. -->
-
-    <selections>
-      <selection start="..." end="..."/>
-    </selections>
-
-
-    </sv>
- 
-\endverbatim
- */
-
-
-class SVFileReader : public QXmlDefaultHandler
-{
-public:
-    SVFileReader(Document *document,
-		 SVFileReaderPaneCallback &callback,
-                 QString location = ""); // for audio file locate mechanism
-    virtual ~SVFileReader();
-
-    void parse(const QString &xmlData);
-    void parse(QXmlInputSource &source);
-
-    bool isOK();
-    QString getErrorString() const { return m_errorString; }
-
-    // For loading a single layer onto an existing pane
-    void setCurrentPane(Pane *pane) { m_currentPane = pane; }
-    
-    virtual bool startElement(const QString &namespaceURI,
-			      const QString &localName,
-			      const QString &qName,
-			      const QXmlAttributes& atts);
-
-    virtual bool characters(const QString &);
-
-    virtual bool endElement(const QString &namespaceURI,
-			    const QString &localName,
-			    const QString &qName);
-
-    bool error(const QXmlParseException &exception);
-    bool fatalError(const QXmlParseException &exception);
-
-protected:
-    bool readWindow(const QXmlAttributes &);
-    bool readModel(const QXmlAttributes &);
-    bool readView(const QXmlAttributes &);
-    bool readLayer(const QXmlAttributes &);
-    bool readDatasetStart(const QXmlAttributes &);
-    bool addBinToDataset(const QXmlAttributes &);
-    bool addPointToDataset(const QXmlAttributes &);
-    bool addRowToDataset(const QXmlAttributes &);
-    bool readRowData(const QString &);
-    bool readDerivation(const QXmlAttributes &);
-    bool readPlayParameters(const QXmlAttributes &);
-    bool readPlugin(const QXmlAttributes &);
-    bool readSelection(const QXmlAttributes &);
-    bool readMeasurement(const QXmlAttributes &);
-    void addUnaddedModels();
-
-    bool haveModel(int id) {
-        return (m_models.find(id) != m_models.end()) && m_models[id];
-    }
-
-    Document *m_document;
-    SVFileReaderPaneCallback &m_paneCallback;
-    QString m_location;
-    Pane *m_currentPane;
-    std::map<int, Layer *> m_layers;
-    std::map<int, Model *> m_models;
-    std::set<Model *> m_addedModels;
-    std::map<int, int> m_awaitingDatasets; // map dataset id -> model id
-    Layer *m_currentLayer;
-    Model *m_currentDataset;
-    Model *m_currentDerivedModel;
-    int m_currentDerivedModelId;
-    PlayParameters *m_currentPlayParameters;
-    QString m_currentTransform;
-    Model *m_currentTransformSource;
-    PluginTransform::ExecutionContext m_currentTransformContext;
-    QString m_currentTransformConfiguration;
-    QString m_datasetSeparator;
-    bool m_inRow;
-    bool m_inLayer;
-    bool m_inView;
-    bool m_inData;
-    bool m_inSelections;
-    int m_rowNumber;
-    QString m_errorString;
-    bool m_ok;
-};
-
-#endif
--- a/main/MainWindow.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ b/main/MainWindow.cpp	Wed Oct 24 16:34:31 2007 +0000
@@ -60,7 +60,7 @@
 #include "data/fileio/FileSource.h"
 #include "data/fft/FFTDataServer.h"
 #include "base/RecentFiles.h"
-#include "transform/TransformFactory.h"
+#include "plugin/transform/TransformFactory.h"
 #include "base/PlayParameterRepository.h"
 #include "base/XmlExportable.h"
 #include "base/CommandHistory.h"
@@ -68,7 +68,7 @@
 #include "base/Clipboard.h"
 #include "base/UnitDatabase.h"
 #include "base/ColourDatabase.h"
-#include "osc/OSCQueue.h"
+#include "data/osc/OSCQueue.h"
 
 // For version information
 #include "vamp/vamp.h"
--- a/main/MainWindow.h	Wed Oct 24 16:00:30 2007 +0000
+++ b/main/MainWindow.h	Wed Oct 24 16:34:31 2007 +0000
@@ -21,14 +21,13 @@
 #include <QUrl>
 #include <QPointer>
 
-#include "MainWindowBase.h"
-
+#include "document/MainWindowBase.h"
 #include "base/Command.h"
 #include "view/ViewManager.h"
 #include "base/PropertyContainer.h"
 #include "base/RecentFiles.h"
 #include "layer/LayerFactory.h"
-#include "transform/Transform.h"
+#include "plugin/transform/Transform.h"
 #include "document/SVFileReader.h"
 #include "data/fileio/FileFinder.h"
 #include "data/fileio/FileSource.h"
--- a/main/MainWindowBase.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2006 +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-2007 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 "MainWindowBase.h"
-#include "document/Document.h"
-
-
-#include "view/Pane.h"
-#include "view/PaneStack.h"
-#include "data/model/WaveFileModel.h"
-#include "data/model/SparseOneDimensionalModel.h"
-#include "data/model/NoteModel.h"
-#include "data/model/Labeller.h"
-#include "view/ViewManager.h"
-
-#include "layer/WaveformLayer.h"
-#include "layer/TimeRulerLayer.h"
-#include "layer/TimeInstantLayer.h"
-#include "layer/TimeValueLayer.h"
-#include "layer/Colour3DPlotLayer.h"
-#include "layer/SliceLayer.h"
-#include "layer/SliceableLayer.h"
-#include "layer/ImageLayer.h"
-
-#include "widgets/ListInputDialog.h"
-
-#include "audioio/AudioCallbackPlaySource.h"
-#include "audioio/AudioCallbackPlayTarget.h"
-#include "audioio/AudioTargetFactory.h"
-#include "audioio/PlaySpeedRangeMapper.h"
-#include "data/fileio/DataFileReaderFactory.h"
-#include "data/fileio/PlaylistFileReader.h"
-#include "data/fileio/WavFileWriter.h"
-#include "data/fileio/CSVFileWriter.h"
-#include "data/fileio/MIDIFileWriter.h"
-#include "data/fileio/BZipFileDevice.h"
-#include "data/fileio/FileSource.h"
-
-#include "data/fft/FFTDataServer.h"
-
-#include "base/RecentFiles.h"
-
-#include "base/PlayParameterRepository.h"
-#include "base/XmlExportable.h"
-#include "base/CommandHistory.h"
-#include "base/Profiler.h"
-#include "base/Preferences.h"
-
-#include "osc/OSCQueue.h"
-
-#include <QApplication>
-#include <QMessageBox>
-#include <QGridLayout>
-#include <QLabel>
-#include <QAction>
-#include <QMenuBar>
-#include <QToolBar>
-#include <QInputDialog>
-#include <QStatusBar>
-#include <QTreeView>
-#include <QFile>
-#include <QFileInfo>
-#include <QDir>
-#include <QTextStream>
-#include <QProcess>
-#include <QShortcut>
-#include <QSettings>
-#include <QDateTime>
-#include <QProcess>
-#include <QCheckBox>
-#include <QRegExp>
-#include <QScrollArea>
-
-#include <iostream>
-#include <cstdio>
-#include <errno.h>
-
-using std::cerr;
-using std::endl;
-
-using std::vector;
-using std::map;
-using std::set;
-
-
-MainWindowBase::MainWindowBase(bool withAudioOutput, bool withOSCSupport) :
-    m_document(0),
-    m_paneStack(0),
-    m_viewManager(0),
-    m_timeRulerLayer(0),
-    m_audioOutput(withAudioOutput),
-    m_playSource(0),
-    m_playTarget(0),
-    m_oscQueue(withOSCSupport ? new OSCQueue() : 0),
-    m_recentFiles("RecentFiles", 20),
-    m_recentTransforms("RecentTransforms", 20),
-    m_documentModified(false),
-    m_openingAudioFile(false),
-    m_abandoning(false),
-    m_labeller(0)
-{
-    connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
-	    this, SLOT(documentModified()));
-    connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
-	    this, SLOT(documentRestored()));
-    
-    m_viewManager = new ViewManager();
-    connect(m_viewManager, SIGNAL(selectionChanged()),
-	    this, SLOT(updateMenuStates()));
-    connect(m_viewManager, SIGNAL(inProgressSelectionChanged()),
-	    this, SLOT(inProgressSelectionChanged()));
-
-    Preferences::BackgroundMode mode =
-        Preferences::getInstance()->getBackgroundMode();
-    m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
-    if (mode != Preferences::BackgroundFromTheme) {
-        m_viewManager->setGlobalDarkBackground
-            (mode == Preferences::DarkBackground);
-    }
-
-    m_paneStack = new PaneStack(0, m_viewManager);
-    connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
-	    this, SLOT(currentPaneChanged(Pane *)));
-    connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
-	    this, SLOT(currentLayerChanged(Pane *, Layer *)));
-    connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)),
-            this, SLOT(rightButtonMenuRequested(Pane *, QPoint)));
-    connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)),
-            this, SLOT(contextHelpChanged(const QString &)));
-    connect(m_paneStack, SIGNAL(paneAdded(Pane *)),
-            this, SLOT(paneAdded(Pane *)));
-    connect(m_paneStack, SIGNAL(paneHidden(Pane *)),
-            this, SLOT(paneHidden(Pane *)));
-    connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)),
-            this, SLOT(paneAboutToBeDeleted(Pane *)));
-    connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)),
-            this, SLOT(paneDropAccepted(Pane *, QStringList)));
-    connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)),
-            this, SLOT(paneDropAccepted(Pane *, QString)));
-
-    m_playSource = new AudioCallbackPlaySource(m_viewManager);
-
-    connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)),
-	    this,           SLOT(sampleRateMismatch(size_t, size_t, bool)));
-    connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
-            this,           SLOT(audioOverloadPluginDisabled()));
-
-    connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)),
-	    this, SLOT(outputLevelsChanged(float, float)));
-
-    connect(m_viewManager, SIGNAL(playbackFrameChanged(unsigned long)),
-            this, SLOT(playbackFrameChanged(unsigned long)));
-
-    connect(m_viewManager, SIGNAL(globalCentreFrameChanged(unsigned long)),
-            this, SLOT(globalCentreFrameChanged(unsigned long)));
-
-    connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, unsigned long)),
-            this, SLOT(viewCentreFrameChanged(View *, unsigned long)));
-
-    connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, unsigned long, bool)),
-            this, SLOT(viewZoomLevelChanged(View *, unsigned long, bool)));
-
-    connect(Preferences::getInstance(),
-            SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
-            this,
-            SLOT(preferenceChanged(PropertyContainer::PropertyName)));
-
-    if (m_oscQueue && m_oscQueue->isOK()) {
-        connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
-        QTimer *oscTimer = new QTimer(this);
-        connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
-        oscTimer->start(1000);
-    }
-
-    Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter;
-    QSettings settings;
-    settings.beginGroup("MainWindow");
-    labellerType = (Labeller::ValueType)
-        settings.value("labellertype", (int)labellerType).toInt();
-    int cycle = settings.value("labellercycle", 4).toInt();
-    settings.endGroup();
-
-    m_labeller = new Labeller(labellerType);
-    m_labeller->setCounterCycleSize(cycle);
-}
-
-MainWindowBase::~MainWindowBase()
-{
-    delete m_playTarget;
-    delete m_playSource;
-    delete m_viewManager;
-    delete m_oscQueue;
-    Profiles::getInstance()->dump();
-}
-
-QString
-MainWindowBase::getOpenFileName(FileFinder::FileType type)
-{
-    FileFinder *ff = FileFinder::getInstance();
-    switch (type) {
-    case FileFinder::SessionFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::AudioFile:
-        return ff->getOpenFileName(type, m_audioFile);
-    case FileFinder::LayerFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::LayerFileNoMidi:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::SessionOrAudioFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::ImageFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::AnyFile:
-        if (getMainModel() != 0 &&
-            m_paneStack != 0 &&
-            m_paneStack->getCurrentPane() != 0) { // can import a layer
-            return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile);
-        } else {
-            return ff->getOpenFileName(FileFinder::SessionOrAudioFile,
-                                       m_sessionFile);
-        }
-    }
-    return "";
-}
-
-QString
-MainWindowBase::getSaveFileName(FileFinder::FileType type)
-{
-    FileFinder *ff = FileFinder::getInstance();
-    switch (type) {
-    case FileFinder::SessionFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::AudioFile:
-        return ff->getSaveFileName(type, m_audioFile);
-    case FileFinder::LayerFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::LayerFileNoMidi:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::SessionOrAudioFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::ImageFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::AnyFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    }
-    return "";
-}
-
-void
-MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path)
-{
-    FileFinder *ff = FileFinder::getInstance();
-    ff->registerLastOpenedFilePath(type, path);
-}
-
-void
-MainWindowBase::updateMenuStates()
-{
-    Pane *currentPane = 0;
-    Layer *currentLayer = 0;
-
-    if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentLayer = currentPane->getSelectedLayer();
-
-    bool haveCurrentPane =
-        (currentPane != 0);
-    bool haveCurrentLayer =
-        (haveCurrentPane &&
-         (currentLayer != 0));
-    bool haveMainModel =
-	(getMainModel() != 0);
-    bool havePlayTarget =
-	(m_playTarget != 0);
-    bool haveSelection = 
-	(m_viewManager &&
-	 !m_viewManager->getSelections().empty());
-    bool haveCurrentEditableLayer =
-	(haveCurrentLayer &&
-	 currentLayer->isLayerEditable());
-    bool haveCurrentTimeInstantsLayer = 
-	(haveCurrentLayer &&
-	 dynamic_cast<TimeInstantLayer *>(currentLayer));
-    bool haveCurrentColour3DPlot =
-        (haveCurrentLayer &&
-         dynamic_cast<Colour3DPlotLayer *>(currentLayer));
-    bool haveClipboardContents =
-        (m_viewManager &&
-         !m_viewManager->getClipboard().empty());
-
-    emit canAddPane(haveMainModel);
-    emit canDeleteCurrentPane(haveCurrentPane);
-    emit canZoom(haveMainModel && haveCurrentPane);
-    emit canScroll(haveMainModel && haveCurrentPane);
-    emit canAddLayer(haveMainModel && haveCurrentPane);
-    emit canImportMoreAudio(haveMainModel);
-    emit canImportLayer(haveMainModel && haveCurrentPane);
-    emit canExportAudio(haveMainModel);
-    emit canExportLayer(haveMainModel &&
-                        (haveCurrentEditableLayer || haveCurrentColour3DPlot));
-    emit canExportImage(haveMainModel && haveCurrentPane);
-    emit canDeleteCurrentLayer(haveCurrentLayer);
-    emit canRenameLayer(haveCurrentLayer);
-    emit canEditLayer(haveCurrentEditableLayer);
-    emit canMeasureLayer(haveCurrentLayer);
-    emit canSelect(haveMainModel && haveCurrentPane);
-    emit canPlay(havePlayTarget);
-    emit canFfwd(true);
-    emit canRewind(true);
-    emit canPaste(haveCurrentEditableLayer && haveClipboardContents);
-    emit canInsertInstant(haveCurrentPane);
-    emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
-    emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection);
-    emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
-    emit canClearSelection(haveSelection);
-    emit canEditSelection(haveSelection && haveCurrentEditableLayer);
-    emit canSave(m_sessionFile != "" && m_documentModified);
-}
-
-void
-MainWindowBase::documentModified()
-{
-//    std::cerr << "MainWindowBase::documentModified" << std::endl;
-
-    if (!m_documentModified) {
-        //!!! this in subclass implementation?
-	setWindowTitle(tr("%1 (modified)").arg(windowTitle()));
-    }
-
-    m_documentModified = true;
-    updateMenuStates();
-}
-
-void
-MainWindowBase::documentRestored()
-{
-//    std::cerr << "MainWindowBase::documentRestored" << std::endl;
-
-    if (m_documentModified) {
-        //!!! this in subclass implementation?
-	QString wt(windowTitle());
-	wt.replace(tr(" (modified)"), "");
-	setWindowTitle(wt);
-    }
-
-    m_documentModified = false;
-    updateMenuStates();
-}
-
-void
-MainWindowBase::playLoopToggled()
-{
-    QAction *action = dynamic_cast<QAction *>(sender());
-    
-    if (action) {
-	m_viewManager->setPlayLoopMode(action->isChecked());
-    } else {
-	m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode());
-    }
-}
-
-void
-MainWindowBase::playSelectionToggled()
-{
-    QAction *action = dynamic_cast<QAction *>(sender());
-    
-    if (action) {
-	m_viewManager->setPlaySelectionMode(action->isChecked());
-    } else {
-	m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode());
-    }
-}
-
-void
-MainWindowBase::playSoloToggled()
-{
-    QAction *action = dynamic_cast<QAction *>(sender());
-    
-    if (action) {
-	m_viewManager->setPlaySoloMode(action->isChecked());
-    } else {
-	m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode());
-    }
-
-    if (m_viewManager->getPlaySoloMode()) {
-        currentPaneChanged(m_paneStack->getCurrentPane());
-    } else {
-        m_viewManager->setPlaybackModel(0);
-        if (m_playSource) {
-            m_playSource->clearSoloModelSet();
-        }
-    }
-}
-
-void
-MainWindowBase::currentPaneChanged(Pane *p)
-{
-    updateMenuStates();
-    updateVisibleRangeDisplay(p);
-
-    if (!p) return;
-
-    if (!(m_viewManager &&
-          m_playSource &&
-          m_viewManager->getPlaySoloMode())) {
-        if (m_viewManager) m_viewManager->setPlaybackModel(0);
-        return;
-    }
-
-    Model *prevPlaybackModel = m_viewManager->getPlaybackModel();
-
-    View::ModelSet soloModels = p->getModels();
-    
-    for (View::ModelSet::iterator mi = soloModels.begin();
-         mi != soloModels.end(); ++mi) {
-        if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) {
-            m_viewManager->setPlaybackModel(*mi);
-        }
-    }
-    
-    RangeSummarisableTimeValueModel *a = 
-        dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel);
-    RangeSummarisableTimeValueModel *b = 
-        dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager->
-                                                        getPlaybackModel());
-
-    m_playSource->setSoloModelSet(soloModels);
-
-    if (a && b && (a != b)) {
-        int frame = m_playSource->getCurrentPlayingFrame();
-        //!!! I don't really believe that these functions are the right way around
-        int rframe = a->alignFromReference(frame);
-        int bframe = b->alignToReference(rframe);
-        if (m_playSource->isPlaying()) m_playSource->play(bframe);
-    }
-}
-
-void
-MainWindowBase::currentLayerChanged(Pane *p, Layer *)
-{
-    updateMenuStates();
-    updateVisibleRangeDisplay(p);
-}
-
-void
-MainWindowBase::selectAll()
-{
-    if (!getMainModel()) return;
-    m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
-					  getMainModel()->getEndFrame()));
-}
-
-void
-MainWindowBase::selectToStart()
-{
-    if (!getMainModel()) return;
-    m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
-					  m_viewManager->getGlobalCentreFrame()));
-}
-
-void
-MainWindowBase::selectToEnd()
-{
-    if (!getMainModel()) return;
-    m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(),
-					  getMainModel()->getEndFrame()));
-}
-
-void
-MainWindowBase::selectVisible()
-{
-    Model *model = getMainModel();
-    if (!model) return;
-
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    size_t startFrame, endFrame;
-
-    if (currentPane->getStartFrame() < 0) startFrame = 0;
-    else startFrame = currentPane->getStartFrame();
-
-    if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame();
-    else endFrame = currentPane->getEndFrame();
-
-    m_viewManager->setSelection(Selection(startFrame, endFrame));
-}
-
-void
-MainWindowBase::clearSelection()
-{
-    m_viewManager->clearSelections();
-}
-
-void
-MainWindowBase::cut()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    Layer *layer = currentPane->getSelectedLayer();
-    if (!layer) return;
-
-    Clipboard &clipboard = m_viewManager->getClipboard();
-    clipboard.clear();
-
-    MultiSelection::SelectionList selections = m_viewManager->getSelections();
-
-    CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true);
-
-    for (MultiSelection::SelectionList::iterator i = selections.begin();
-         i != selections.end(); ++i) {
-        layer->copy(*i, clipboard);
-        layer->deleteSelection(*i);
-    }
-
-    CommandHistory::getInstance()->endCompoundOperation();
-}
-
-void
-MainWindowBase::copy()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    Layer *layer = currentPane->getSelectedLayer();
-    if (!layer) return;
-
-    Clipboard &clipboard = m_viewManager->getClipboard();
-    clipboard.clear();
-
-    MultiSelection::SelectionList selections = m_viewManager->getSelections();
-
-    for (MultiSelection::SelectionList::iterator i = selections.begin();
-         i != selections.end(); ++i) {
-        layer->copy(*i, clipboard);
-    }
-}
-
-void
-MainWindowBase::paste()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    //!!! if we have no current layer, we should create one of the most
-    // appropriate type
-
-    Layer *layer = currentPane->getSelectedLayer();
-    if (!layer) return;
-
-    Clipboard &clipboard = m_viewManager->getClipboard();
-    Clipboard::PointList contents = clipboard.getPoints();
-/*
-    long minFrame = 0;
-    bool have = false;
-    for (int i = 0; i < contents.size(); ++i) {
-        if (!contents[i].haveFrame()) continue;
-        if (!have || contents[i].getFrame() < minFrame) {
-            minFrame = contents[i].getFrame();
-            have = true;
-        }
-    }
-
-    long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame;
-
-    layer->paste(clipboard, frameOffset);
-*/
-    layer->paste(clipboard, 0, true);
-}
-
-void
-MainWindowBase::deleteSelected()
-{
-    if (m_paneStack->getCurrentPane() &&
-	m_paneStack->getCurrentPane()->getSelectedLayer()) {
-        
-        Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer();
-
-        if (m_viewManager && 
-            (m_viewManager->getToolMode() == ViewManager::MeasureMode)) {
-
-            layer->deleteCurrentMeasureRect();
-
-        } else {
-
-            MultiSelection::SelectionList selections =
-                m_viewManager->getSelections();
-            
-            for (MultiSelection::SelectionList::iterator i = selections.begin();
-                 i != selections.end(); ++i) {
-                layer->deleteSelection(*i);
-            }
-	}
-    }
-}
-
-void
-MainWindowBase::insertInstant()
-{
-    int frame = m_viewManager->getPlaybackFrame();
-    insertInstantAt(frame);
-}
-
-void
-MainWindowBase::insertInstantsAtBoundaries()
-{
-    MultiSelection::SelectionList selections = m_viewManager->getSelections();
-    for (MultiSelection::SelectionList::iterator i = selections.begin();
-         i != selections.end(); ++i) {
-        size_t start = i->getStartFrame();
-        size_t end = i->getEndFrame();
-        if (start != end) {
-            insertInstantAt(i->getStartFrame());
-            insertInstantAt(i->getEndFrame());
-        }
-    }
-}
-
-void
-MainWindowBase::insertInstantAt(size_t frame)
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (!pane) {
-        return;
-    }
-
-    Layer *layer = dynamic_cast<TimeInstantLayer *>
-        (pane->getSelectedLayer());
-
-    if (!layer) {
-        for (int i = pane->getLayerCount(); i > 0; --i) {
-            layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1));
-            if (layer) break;
-        }
-
-        if (!layer) {
-            CommandHistory::getInstance()->startCompoundOperation
-                (tr("Add Point"), true);
-            layer = m_document->createEmptyLayer(LayerFactory::TimeInstants);
-            if (layer) {
-                m_document->addLayerToView(pane, layer);
-                m_paneStack->setCurrentLayer(pane, layer);
-            }
-            CommandHistory::getInstance()->endCompoundOperation();
-        }
-    }
-
-    if (layer) {
-    
-        Model *model = layer->getModel();
-        SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
-            (model);
-
-        if (sodm) {
-            SparseOneDimensionalModel::Point point(frame, "");
-
-            SparseOneDimensionalModel::Point prevPoint(0);
-            bool havePrevPoint = false;
-
-            SparseOneDimensionalModel::EditCommand *command =
-                new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point"));
-
-            if (m_labeller->actingOnPrevPoint()) {
-
-                SparseOneDimensionalModel::PointList prevPoints =
-                    sodm->getPreviousPoints(frame);
-
-                if (!prevPoints.empty()) {
-                    prevPoint = *prevPoints.begin();
-                    havePrevPoint = true;
-                }
-            }
-
-            if (m_labeller) {
-
-                m_labeller->setSampleRate(sodm->getSampleRate());
-
-                if (havePrevPoint) {
-                    command->deletePoint(prevPoint);
-                }
-
-                m_labeller->label<SparseOneDimensionalModel::Point>
-                    (point, havePrevPoint ? &prevPoint : 0);
-
-                if (havePrevPoint) {
-                    command->addPoint(prevPoint);
-                }
-            }
-            
-            command->addPoint(point);
-
-            command->setName(tr("Add Point at %1 s")
-                             .arg(RealTime::frame2RealTime
-                                  (frame,
-                                   sodm->getSampleRate())
-                                  .toText(false).c_str()));
-
-            command->finish();
-        }
-    }
-}
-
-void
-MainWindowBase::renumberInstants()
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (!pane) return;
-
-    Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
-    if (!layer) return;
-
-    MultiSelection ms(m_viewManager->getSelection());
-    
-    Model *model = layer->getModel();
-    SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
-        (model);
-    if (!sodm) return;
-
-    if (!m_labeller) return;
-
-    Labeller labeller(*m_labeller);
-    labeller.setSampleRate(sodm->getSampleRate());
-
-    // This uses a command
-
-    labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms);
-}
-
-MainWindowBase::FileOpenStatus
-MainWindowBase::open(QString fileOrUrl, AudioFileOpenMode mode)
-{
-    return open(FileSource(fileOrUrl), mode);
-}
-
-MainWindowBase::FileOpenStatus
-MainWindowBase::open(FileSource source, AudioFileOpenMode mode)
-{
-    FileOpenStatus status;
-
-    if (!source.isAvailable()) return FileOpenFailed;
-    source.waitForData();
-
-    bool canImportLayer = (getMainModel() != 0 &&
-                           m_paneStack != 0 &&
-                           m_paneStack->getCurrentPane() != 0);
-
-    if ((status = openAudio(source, mode)) != FileOpenFailed) {
-        return status;
-    } else if ((status = openSession(source)) != FileOpenFailed) {
-	return status;
-    } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
-        return status;
-    } else if (!canImportLayer) {
-        return FileOpenWrongMode;
-    } else if ((status = openImage(source)) != FileOpenFailed) {
-        return status;
-    } else if ((status = openLayer(source)) != FileOpenFailed) {
-        return status;
-    } else {
-	return FileOpenFailed;
-    }
-}
-
-MainWindowBase::FileOpenStatus
-MainWindowBase::openAudio(FileSource source, AudioFileOpenMode mode)
-{
-    std::cerr << "MainWindowBase::openAudio(" << source.getLocation().toStdString() << ")" << std::endl;
-
-    if (!source.isAvailable()) return FileOpenFailed;
-    source.waitForData();
-
-    m_openingAudioFile = true;
-
-    size_t rate = 0;
-
-    if (Preferences::getInstance()->getResampleOnLoad()) {
-        rate = m_playSource->getSourceSampleRate();
-    }
-
-    WaveFileModel *newModel = new WaveFileModel(source, rate);
-
-    if (!newModel->isOK()) {
-	delete newModel;
-        m_openingAudioFile = false;
-	return FileOpenFailed;
-    }
-
-    std::cerr << "mode = " << mode << std::endl;
-
-    if (mode == AskUser) {
-        if (getMainModel()) {
-
-            static bool prevSetAsMain = true;
-            bool setAsMain = true;
-            
-            QStringList items;
-            items << tr("Replace the existing main waveform")
-                  << tr("Load this file into a new waveform pane");
-
-            bool ok = false;
-            QString item = ListInputDialog::getItem
-                (this, tr("Select target for import"),
-                 tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"),
-                 items, prevSetAsMain ? 0 : 1, &ok);
-            
-            if (!ok || item.isEmpty()) {
-                delete newModel;
-                m_openingAudioFile = false;
-                return FileOpenCancelled;
-            }
-            
-            setAsMain = (item == items[0]);
-            prevSetAsMain = setAsMain;
-
-            if (setAsMain) mode = ReplaceMainModel;
-            else mode = CreateAdditionalModel;
-
-        } else {
-            mode = ReplaceMainModel;
-        }
-    }
-
-    if (mode == ReplaceCurrentPane) {
-
-        Pane *pane = m_paneStack->getCurrentPane();
-        if (pane) {
-            if (getMainModel()) {
-                View::ModelSet models(pane->getModels());
-                if (models.find(getMainModel()) != models.end()) {
-                    mode = ReplaceMainModel;
-                }
-            } else {
-                mode = ReplaceMainModel;
-            }
-        } else {
-            mode = CreateAdditionalModel;
-        }
-    }
-
-    if (mode == CreateAdditionalModel && !getMainModel()) {
-        mode = ReplaceMainModel;
-    }
-
-    if (mode == ReplaceMainModel) {
-
-        Model *prevMain = getMainModel();
-        if (prevMain) {
-            m_playSource->removeModel(prevMain);
-            PlayParameterRepository::getInstance()->removeModel(prevMain);
-        }
-        PlayParameterRepository::getInstance()->addModel(newModel);
-
-	m_document->setMainModel(newModel);
-
-	setupMenus();
-
-	if (m_sessionFile == "") {
-            //!!! shouldn't be dealing directly with title from here -- call a method
-	    setWindowTitle(tr("Sonic Visualiser: %1")
-                           .arg(source.getLocation()));
-	    CommandHistory::getInstance()->clear();
-	    CommandHistory::getInstance()->documentSaved();
-	    m_documentModified = false;
-	} else {
-	    setWindowTitle(tr("Sonic Visualiser: %1 [%2]")
-			   .arg(QFileInfo(m_sessionFile).fileName())
-			   .arg(source.getLocation()));
-	    if (m_documentModified) {
-		m_documentModified = false;
-		documentModified(); // so as to restore "(modified)" window title
-	    }
-	}
-
-        if (!source.isRemote()) m_audioFile = source.getLocalFilename();
-
-    } else if (mode == CreateAdditionalModel) {
-
-	CommandHistory::getInstance()->startCompoundOperation
-	    (tr("Import \"%1\"").arg(source.getLocation()), true);
-
-	m_document->addImportedModel(newModel);
-
-	AddPaneCommand *command = new AddPaneCommand(this);
-	CommandHistory::getInstance()->addCommand(command);
-
-	Pane *pane = command->getPane();
-
-	if (!m_timeRulerLayer) {
-	    m_timeRulerLayer = m_document->createMainModelLayer
-		(LayerFactory::TimeRuler);
-	}
-
-	m_document->addLayerToView(pane, m_timeRulerLayer);
-
-	Layer *newLayer = m_document->createImportedLayer(newModel);
-
-	if (newLayer) {
-	    m_document->addLayerToView(pane, newLayer);
-	}
-	
-	CommandHistory::getInstance()->endCompoundOperation();
-
-    } else if (mode == ReplaceCurrentPane) {
-
-        // We know there is a current pane, otherwise we would have
-        // reset the mode to CreateAdditionalModel above; and we know
-        // the current pane does not contain the main model, otherwise
-        // we would have reset it to ReplaceMainModel.  But we don't
-        // know whether the pane contains a waveform model at all.
-        
-        Pane *pane = m_paneStack->getCurrentPane();
-        Layer *replace = 0;
-
-        for (int i = 0; i < pane->getLayerCount(); ++i) {
-            Layer *layer = pane->getLayer(i);
-            if (dynamic_cast<WaveformLayer *>(layer)) {
-                replace = layer;
-                break;
-            }
-        }
-
-	CommandHistory::getInstance()->startCompoundOperation
-	    (tr("Import \"%1\"").arg(source.getLocation()), true);
-
-	m_document->addImportedModel(newModel);
-
-        if (replace) {
-            m_document->removeLayerFromView(pane, replace);
-        }
-
-	Layer *newLayer = m_document->createImportedLayer(newModel);
-
-	if (newLayer) {
-	    m_document->addLayerToView(pane, newLayer);
-	}
-	
-	CommandHistory::getInstance()->endCompoundOperation();
-    }
-
-    updateMenuStates();
-    m_recentFiles.addFile(source.getLocation());
-    if (!source.isRemote()) {
-        // for file dialog
-        registerLastOpenedFilePath(FileFinder::AudioFile,
-                                   source.getLocalFilename());
-    }
-    m_openingAudioFile = false;
-
-    currentPaneChanged(m_paneStack->getCurrentPane());
-
-    return FileOpenSucceeded;
-}
-
-MainWindowBase::FileOpenStatus
-MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode)
-{
-    std::set<QString> extensions;
-    PlaylistFileReader::getSupportedExtensions(extensions);
-    QString extension = source.getExtension();
-    if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
-
-    if (!source.isAvailable()) return FileOpenFailed;
-    source.waitForData();
-
-    PlaylistFileReader reader(source.getLocalFilename());
-    if (!reader.isOK()) return FileOpenFailed;
-
-    PlaylistFileReader::Playlist playlist = reader.load();
-
-    bool someSuccess = false;
-
-    for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
-         i != playlist.end(); ++i) {
-
-        FileOpenStatus status = openAudio(*i, mode);
-
-        if (status == FileOpenCancelled) {
-            return FileOpenCancelled;
-        }
-
-        if (status == FileOpenSucceeded) {
-            someSuccess = true;
-            mode = CreateAdditionalModel;
-        }
-    }
-
-    if (someSuccess) return FileOpenSucceeded;
-    else return FileOpenFailed;
-}
-
-MainWindowBase::FileOpenStatus
-MainWindowBase::openLayer(FileSource source)
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    
-    if (!pane) {
-	// shouldn't happen, as the menu action should have been disabled
-	std::cerr << "WARNING: MainWindowBase::openLayer: no current pane" << std::endl;
-	return FileOpenWrongMode;
-    }
-
-    if (!getMainModel()) {
-	// shouldn't happen, as the menu action should have been disabled
-	std::cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << std::endl;
-	return FileOpenWrongMode;
-    }
-
-    if (!source.isAvailable()) return FileOpenFailed;
-    source.waitForData();
-
-    QString path = source.getLocalFilename();
-
-    if (source.getExtension() == "svl" || source.getExtension() == "xml") {
-
-        PaneCallback callback(this);
-        QFile file(path);
-        
-        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
-            std::cerr << "ERROR: MainWindowBase::openLayer("
-                      << source.getLocation().toStdString()
-                      << "): Failed to open file for reading" << std::endl;
-            return FileOpenFailed;
-        }
-        
-        SVFileReader reader(m_document, callback, source.getLocation());
-        reader.setCurrentPane(pane);
-        
-        QXmlInputSource inputSource(&file);
-        reader.parse(inputSource);
-        
-        if (!reader.isOK()) {
-            std::cerr << "ERROR: MainWindowBase::openLayer("
-                      << source.getLocation().toStdString()
-                      << "): Failed to read XML file: "
-                      << reader.getErrorString().toStdString() << std::endl;
-            return FileOpenFailed;
-        }
-
-        m_recentFiles.addFile(source.getLocation());
-
-        if (!source.isRemote()) {
-            registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
-        }
-
-    } else {
-        
-        try {
-
-            Model *model = DataFileReaderFactory::load
-                (path, getMainModel()->getSampleRate());
-        
-            if (model) {
-
-                std::cerr << "MainWindowBase::openLayer: Have model" << std::endl;
-
-                Layer *newLayer = m_document->createImportedLayer(model);
-
-                if (newLayer) {
-
-                    m_document->addLayerToView(pane, newLayer);
-                    m_recentFiles.addFile(source.getLocation());
-                    
-                    if (!source.isRemote()) {
-                        registerLastOpenedFilePath
-                            (FileFinder::LayerFile,
-                             path); // for file dialog
-                    }
-                    
-                    return FileOpenSucceeded;
-                }
-            }
-        } catch (DataFileReaderFactory::Exception e) {
-            if (e == DataFileReaderFactory::ImportCancelled) {
-                return FileOpenCancelled;
-            }
-        }
-    }
-    
-    source.setLeaveLocalFile(true);
-    return FileOpenFailed;
-}
-
-MainWindowBase::FileOpenStatus
-MainWindowBase::openImage(FileSource source)
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    
-    if (!pane) {
-	// shouldn't happen, as the menu action should have been disabled
-	std::cerr << "WARNING: MainWindowBase::openImage: no current pane" << std::endl;
-	return FileOpenWrongMode;
-    }
-
-    if (!m_document->getMainModel()) {
-        return FileOpenWrongMode;
-    }
-
-    bool newLayer = false;
-
-    ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
-    if (!il) {
-        for (int i = pane->getLayerCount()-1; i >= 0; --i) {
-            il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
-            if (il) break;
-        }
-    }
-    if (!il) {
-        il = dynamic_cast<ImageLayer *>
-            (m_document->createEmptyLayer(LayerFactory::Image));
-        if (!il) return FileOpenFailed;
-        newLayer = true;
-    }
-
-    // We don't put the image file in Recent Files
-
-    std::cerr << "openImage: trying location \"" << source.getLocation().toStdString() << "\" in image layer" << std::endl;
-
-    if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
-        if (newLayer) {
-            m_document->setModel(il, 0); // releasing its model
-            delete il;
-        }
-        return FileOpenFailed;
-    } else {
-        if (newLayer) {
-            m_document->addLayerToView(pane, il);
-        }
-        m_paneStack->setCurrentLayer(pane, il);
-    }
-
-    return FileOpenSucceeded;
-}
-
-MainWindowBase::FileOpenStatus
-MainWindowBase::openSessionFile(QString fileOrUrl)
-{
-    return openSession(FileSource(fileOrUrl));
-}
-
-MainWindowBase::FileOpenStatus
-MainWindowBase::openSession(FileSource source)
-{
-    if (!source.isAvailable()) return FileOpenFailed;
-    if (source.getExtension() != "sv") return FileOpenFailed;
-    source.waitForData();
-
-    BZipFileDevice bzFile(source.getLocalFilename());
-    if (!bzFile.open(QIODevice::ReadOnly)) return FileOpenFailed;
-
-    if (!checkSaveModified()) return FileOpenCancelled;
-
-    QString error;
-    closeSession();
-    createDocument();
-
-    PaneCallback callback(this);
-    m_viewManager->clearSelections();
-
-    SVFileReader reader(m_document, callback, source.getLocation());
-    QXmlInputSource inputSource(&bzFile);
-    reader.parse(inputSource);
-    
-    if (!reader.isOK()) {
-        error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
-    }
-    
-    bzFile.close();
-
-    bool ok = (error == "");
-
-    if (ok) {
-
-	setWindowTitle(tr("Sonic Visualiser: %1")
-		       .arg(source.getLocation()));
-
-	if (!source.isRemote()) m_sessionFile = source.getLocalFilename();
-
-	setupMenus();
-
-	CommandHistory::getInstance()->clear();
-	CommandHistory::getInstance()->documentSaved();
-	m_documentModified = false;
-	updateMenuStates();
-
-        m_recentFiles.addFile(source.getLocation());
-
-        if (!source.isRemote()) {
-            // for file dialog
-            registerLastOpenedFilePath(FileFinder::SessionFile,
-                                        source.getLocalFilename());
-        }
-
-    } else {
-	setWindowTitle(tr("Sonic Visualiser"));
-    }
-
-    return ok ? FileOpenSucceeded : FileOpenFailed;
-}
-
-void
-MainWindowBase::createPlayTarget()
-{
-    if (m_playTarget) return;
-
-    m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource);
-    if (!m_playTarget) {
-	QMessageBox::warning
-	    (this, tr("Couldn't open audio device"),
-	     tr("<b>No audio available</b><p>Could not open an audio device for playback.<p>Audio playback will not be available during this session."),
-	     QMessageBox::Ok);
-    }
-}
-
-WaveFileModel *
-MainWindowBase::getMainModel()
-{
-    if (!m_document) return 0;
-    return m_document->getMainModel();
-}
-
-const WaveFileModel *
-MainWindowBase::getMainModel() const
-{
-    if (!m_document) return 0;
-    return m_document->getMainModel();
-}
-
-void
-MainWindowBase::createDocument()
-{
-    m_document = new Document;
-
-    connect(m_document, SIGNAL(layerAdded(Layer *)),
-	    this, SLOT(layerAdded(Layer *)));
-    connect(m_document, SIGNAL(layerRemoved(Layer *)),
-	    this, SLOT(layerRemoved(Layer *)));
-    connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
-	    this, SLOT(layerAboutToBeDeleted(Layer *)));
-    connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
-	    this, SLOT(layerInAView(Layer *, bool)));
-
-    connect(m_document, SIGNAL(modelAdded(Model *)),
-	    this, SLOT(modelAdded(Model *)));
-    connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)),
-	    this, SLOT(mainModelChanged(WaveFileModel *)));
-    connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
-	    this, SLOT(modelAboutToBeDeleted(Model *)));
-
-    connect(m_document, SIGNAL(modelGenerationFailed(QString)),
-            this, SLOT(modelGenerationFailed(QString)));
-    connect(m_document, SIGNAL(modelRegenerationFailed(QString, QString)),
-            this, SLOT(modelRegenerationFailed(QString, QString)));
-}
-
-bool
-MainWindowBase::saveSessionFile(QString path)
-{
-    BZipFileDevice bzFile(path);
-    if (!bzFile.open(QIODevice::WriteOnly)) {
-        std::cerr << "Failed to open session file \"" << path.toStdString()
-                  << "\" for writing: "
-                  << bzFile.errorString().toStdString() << std::endl;
-        return false;
-    }
-
-    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
-
-    QTextStream out(&bzFile);
-    toXml(out);
-    out.flush();
-
-    QApplication::restoreOverrideCursor();
-
-    if (!bzFile.isOK()) {
-	QMessageBox::critical(this, tr("Failed to write file"),
-			      tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
-			      .arg(path).arg(bzFile.errorString()));
-        bzFile.close();
-	return false;
-    }
-
-    bzFile.close();
-    return true;
-}
-
-void
-MainWindowBase::toXml(QTextStream &out)
-{
-    QString indent("  ");
-
-    out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
-    out << "<!DOCTYPE sonic-visualiser>\n";
-    out << "<sv>\n";
-
-    m_document->toXml(out, "", "");
-
-    out << "<display>\n";
-
-    out << QString("  <window width=\"%1\" height=\"%2\"/>\n")
-	.arg(width()).arg(height());
-
-    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
-
-	Pane *pane = m_paneStack->getPane(i);
-
-	if (pane) {
-            pane->toXml(out, indent);
-	}
-    }
-
-    out << "</display>\n";
-
-    m_viewManager->getSelection().toXml(out);
-
-    out << "</sv>\n";
-}
-
-Pane *
-MainWindowBase::addPaneToStack()
-{
-    AddPaneCommand *command = new AddPaneCommand(this);
-    CommandHistory::getInstance()->addCommand(command);
-    return command->getPane();
-}
-
-void
-MainWindowBase::zoomIn()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->zoom(true);
-}
-
-void
-MainWindowBase::zoomOut()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->zoom(false);
-}
-
-void
-MainWindowBase::zoomToFit()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (!currentPane) return;
-
-    Model *model = getMainModel();
-    if (!model) return;
-    
-    size_t start = model->getStartFrame();
-    size_t end = model->getEndFrame();
-    size_t pixels = currentPane->width();
-
-    size_t sw = currentPane->getVerticalScaleWidth();
-    if (pixels > sw * 2) pixels -= sw * 2;
-    else pixels = 1;
-    if (pixels > 4) pixels -= 4;
-
-    size_t zoomLevel = (end - start) / pixels;
-
-    currentPane->setZoomLevel(zoomLevel);
-    currentPane->setCentreFrame((start + end) / 2);
-}
-
-void
-MainWindowBase::zoomDefault()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->setZoomLevel(1024);
-}
-
-void
-MainWindowBase::scrollLeft()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->scroll(false, false);
-}
-
-void
-MainWindowBase::jumpLeft()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->scroll(false, true);
-}
-
-void
-MainWindowBase::scrollRight()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->scroll(true, false);
-}
-
-void
-MainWindowBase::jumpRight()
-{
-    Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->scroll(true, true);
-}
-
-void
-MainWindowBase::showNoOverlays()
-{
-    m_viewManager->setOverlayMode(ViewManager::NoOverlays);
-}
-
-void
-MainWindowBase::showMinimalOverlays()
-{
-    m_viewManager->setOverlayMode(ViewManager::MinimalOverlays);
-}
-
-void
-MainWindowBase::showStandardOverlays()
-{
-    m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
-}
-
-void
-MainWindowBase::showAllOverlays()
-{
-    m_viewManager->setOverlayMode(ViewManager::AllOverlays);
-}
-
-void
-MainWindowBase::toggleZoomWheels()
-{
-    if (m_viewManager->getZoomWheelsEnabled()) {
-        m_viewManager->setZoomWheelsEnabled(false);
-    } else {
-        m_viewManager->setZoomWheelsEnabled(true);
-    }
-}
-
-void
-MainWindowBase::togglePropertyBoxes()
-{
-    if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
-        if (Preferences::getInstance()->getPropertyBoxLayout() ==
-            Preferences::VerticallyStacked) {
-            m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
-        } else {
-            m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
-        }
-    } else {
-        m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
-    }
-}
-
-void
-MainWindowBase::toggleStatusBar()
-{
-    QSettings settings;
-    settings.beginGroup("MainWindow");
-    bool sb = settings.value("showstatusbar", true).toBool();
-
-    if (sb) {
-        statusBar()->hide();
-    } else {
-        statusBar()->show();
-    }
-
-    settings.setValue("showstatusbar", !sb);
-
-    settings.endGroup();
-}
-
-void
-MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
-{
-    if (name == "Property Box Layout") {
-        if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
-            if (Preferences::getInstance()->getPropertyBoxLayout() ==
-                Preferences::VerticallyStacked) {
-                m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
-            } else {
-                m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
-            }
-        }
-    } else if (name == "Background Mode" && m_viewManager) {
-        Preferences::BackgroundMode mode =
-            Preferences::getInstance()->getBackgroundMode();
-        if (mode == Preferences::BackgroundFromTheme) {
-            m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
-        } else if (mode == Preferences::DarkBackground) {
-            m_viewManager->setGlobalDarkBackground(true);
-        } else {
-            m_viewManager->setGlobalDarkBackground(false);
-        }
-    }            
-}
-
-void
-MainWindowBase::play()
-{
-    if (m_playSource->isPlaying()) {
-        stop();
-    } else {
-        playbackFrameChanged(m_viewManager->getPlaybackFrame());
-	m_playSource->play(m_viewManager->getPlaybackFrame());
-    }
-}
-
-void
-MainWindowBase::ffwd()
-{
-    if (!getMainModel()) return;
-
-    int frame = m_viewManager->getPlaybackFrame();
-    ++frame;
-
-    Layer *layer = getSnapLayer();
-    size_t sr = getMainModel()->getSampleRate();
-
-    if (!layer) {
-
-        frame = RealTime::realTime2Frame
-            (RealTime::frame2RealTime(frame, sr) + RealTime(2, 0), sr);
-        if (frame > int(getMainModel()->getEndFrame())) {
-            frame = getMainModel()->getEndFrame();
-        }
-
-    } else {
-
-        size_t resolution = 0;
-        if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
-                                       frame, resolution, Layer::SnapRight)) {
-            frame = getMainModel()->getEndFrame();
-        }
-    }
-        
-    if (frame < 0) frame = 0;
-
-    if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(size_t(frame));
-    }
-    
-    m_viewManager->setPlaybackFrame(frame);
-}
-
-void
-MainWindowBase::ffwdEnd()
-{
-    if (!getMainModel()) return;
-
-    size_t frame = getMainModel()->getEndFrame();
-
-    if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(frame);
-    }
-
-    m_viewManager->setPlaybackFrame(frame);
-}
-
-void
-MainWindowBase::rewind()
-{
-    if (!getMainModel()) return;
-
-    int frame = m_viewManager->getPlaybackFrame();
-    if (frame > 0) --frame;
-
-    Layer *layer = getSnapLayer();
-    size_t sr = getMainModel()->getSampleRate();
-    
-    // when rewinding during playback, we want to allow a period
-    // following a rewind target point at which the rewind will go to
-    // the prior point instead of the immediately neighbouring one
-    if (m_playSource && m_playSource->isPlaying()) {
-        RealTime ct = RealTime::frame2RealTime(frame, sr);
-        ct = ct - RealTime::fromSeconds(0.25);
-        if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
-//        std::cerr << "rewind: frame " << frame << " -> ";
-        frame = RealTime::realTime2Frame(ct, sr);
-//        std::cerr << frame << std::endl;
-    }
-
-    if (!layer) {
-        
-        frame = RealTime::realTime2Frame
-            (RealTime::frame2RealTime(frame, sr) - RealTime(2, 0), sr);
-        if (frame < int(getMainModel()->getStartFrame())) {
-            frame = getMainModel()->getStartFrame();
-        }
-
-    } else {
-
-        size_t resolution = 0;
-        if (!layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
-                                       frame, resolution, Layer::SnapLeft)) {
-            frame = getMainModel()->getStartFrame();
-        }
-    }
-
-    if (frame < 0) frame = 0;
-
-    if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(size_t(frame));
-    }
-
-    m_viewManager->setPlaybackFrame(frame);
-}
-
-void
-MainWindowBase::rewindStart()
-{
-    if (!getMainModel()) return;
-
-    size_t frame = getMainModel()->getStartFrame();
-
-    if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(frame);
-    }
-
-    m_viewManager->setPlaybackFrame(frame);
-}
-
-Layer *
-MainWindowBase::getSnapLayer() const
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (!pane) return 0;
-
-    Layer *layer = pane->getSelectedLayer();
-
-    if (!dynamic_cast<TimeInstantLayer *>(layer) &&
-        !dynamic_cast<TimeValueLayer *>(layer) &&
-        !dynamic_cast<TimeRulerLayer *>(layer)) {
-
-        layer = 0;
-
-        for (int i = pane->getLayerCount(); i > 0; --i) {
-            Layer *l = pane->getLayer(i-1);
-            if (dynamic_cast<TimeRulerLayer *>(l)) {
-                layer = l;
-                break;
-            }
-        }
-    }
-
-    return layer;
-}
-
-void
-MainWindowBase::stop()
-{
-    m_playSource->stop();
-
-    if (m_paneStack && m_paneStack->getCurrentPane()) {
-        updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
-    } else {
-        m_myStatusMessage = "";
-        statusBar()->showMessage("");
-    }
-}
-
-MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
-    m_mw(mw),
-    m_pane(0),
-    m_prevCurrentPane(0),
-    m_added(false)
-{
-}
-
-MainWindowBase::AddPaneCommand::~AddPaneCommand()
-{
-    if (m_pane && !m_added) {
-	m_mw->m_paneStack->deletePane(m_pane);
-    }
-}
-
-QString
-MainWindowBase::AddPaneCommand::getName() const
-{
-    return tr("Add Pane");
-}
-
-void
-MainWindowBase::AddPaneCommand::execute()
-{
-    if (!m_pane) {
-	m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
-	m_pane = m_mw->m_paneStack->addPane();
-
-        connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
-                m_mw, SLOT(contextHelpChanged(const QString &)));
-    } else {
-	m_mw->m_paneStack->showPane(m_pane);
-    }
-
-    m_mw->m_paneStack->setCurrentPane(m_pane);
-    m_added = true;
-}
-
-void
-MainWindowBase::AddPaneCommand::unexecute()
-{
-    m_mw->m_paneStack->hidePane(m_pane);
-    m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
-    m_added = false;
-}
-
-MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
-    m_mw(mw),
-    m_pane(pane),
-    m_added(true)
-{
-}
-
-MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
-{
-    if (m_pane && !m_added) {
-	m_mw->m_paneStack->deletePane(m_pane);
-    }
-}
-
-QString
-MainWindowBase::RemovePaneCommand::getName() const
-{
-    return tr("Remove Pane");
-}
-
-void
-MainWindowBase::RemovePaneCommand::execute()
-{
-    m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
-    m_mw->m_paneStack->hidePane(m_pane);
-    m_added = false;
-}
-
-void
-MainWindowBase::RemovePaneCommand::unexecute()
-{
-    m_mw->m_paneStack->showPane(m_pane);
-    m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
-    m_added = true;
-}
-
-void
-MainWindowBase::deleteCurrentPane()
-{
-    CommandHistory::getInstance()->startCompoundOperation
-	(tr("Delete Pane"), true);
-
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (pane) {
-	while (pane->getLayerCount() > 0) {
-	    Layer *layer = pane->getLayer(0);
-	    if (layer) {
-		m_document->removeLayerFromView(pane, layer);
-	    } else {
-		break;
-	    }
-	}
-
-	RemovePaneCommand *command = new RemovePaneCommand(this, pane);
-	CommandHistory::getInstance()->addCommand(command);
-    }
-
-    CommandHistory::getInstance()->endCompoundOperation();
-
-    updateMenuStates();
-}
-
-void
-MainWindowBase::deleteCurrentLayer()
-{
-    Pane *pane = m_paneStack->getCurrentPane();
-    if (pane) {
-	Layer *layer = pane->getSelectedLayer();
-	if (layer) {
-	    m_document->removeLayerFromView(pane, layer);
-	}
-    }
-    updateMenuStates();
-}
-
-void
-MainWindowBase::playbackFrameChanged(unsigned long frame)
-{
-    if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
-
-    RealTime now = RealTime::frame2RealTime
-        (frame, getMainModel()->getSampleRate());
-
-    if (now.sec == m_lastPlayStatusSec) return;
-
-    RealTime then = RealTime::frame2RealTime
-        (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
-
-    QString nowStr;
-    QString thenStr;
-    QString remainingStr;
-
-    if (then.sec > 10) {
-        nowStr = now.toSecText().c_str();
-        thenStr = then.toSecText().c_str();
-        remainingStr = (then - now).toSecText().c_str();
-        m_lastPlayStatusSec = now.sec;
-    } else {
-        nowStr = now.toText(true).c_str();
-        thenStr = then.toText(true).c_str();
-        remainingStr = (then - now).toText(true).c_str();
-    }        
-
-    m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
-        .arg(nowStr).arg(thenStr).arg(remainingStr);
-
-    statusBar()->showMessage(m_myStatusMessage);
-}
-
-void
-MainWindowBase::globalCentreFrameChanged(unsigned long )
-{
-    if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
-    Pane *p = 0;
-    if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
-    if (!p->getFollowGlobalPan()) return;
-    updateVisibleRangeDisplay(p);
-}
-
-void
-MainWindowBase::viewCentreFrameChanged(View *v, unsigned long )
-{
-    if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
-    Pane *p = 0;
-    if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
-    if (v == p) updateVisibleRangeDisplay(p);
-}
-
-void
-MainWindowBase::viewZoomLevelChanged(View *v, unsigned long , bool )
-{
-    if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
-    Pane *p = 0;
-    if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
-    if (v == p) updateVisibleRangeDisplay(p);
-}
-
-void
-MainWindowBase::layerAdded(Layer *)
-{
-//    std::cerr << "MainWindowBase::layerAdded(" << layer << ")" << std::endl;
-    updateMenuStates();
-}
-
-void
-MainWindowBase::layerRemoved(Layer *)
-{
-//    std::cerr << "MainWindowBase::layerRemoved(" << layer << ")" << std::endl;
-    updateMenuStates();
-}
-
-void
-MainWindowBase::layerAboutToBeDeleted(Layer *layer)
-{
-//    std::cerr << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << std::endl;
-    if (layer == m_timeRulerLayer) {
-//	std::cerr << "(this is the time ruler layer)" << std::endl;
-	m_timeRulerLayer = 0;
-    }
-}
-
-void
-MainWindowBase::layerInAView(Layer *layer, bool inAView)
-{
-//    std::cerr << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << std::endl;
-
-    // Check whether we need to add or remove model from play source
-    Model *model = layer->getModel();
-    if (model) {
-        if (inAView) {
-            m_playSource->addModel(model);
-        } else {
-            bool found = false;
-            for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
-                Pane *pane = m_paneStack->getPane(i);
-                if (!pane) continue;
-                for (int j = 0; j < pane->getLayerCount(); ++j) {
-                    Layer *pl = pane->getLayer(j);
-                    if (pl && pl->getModel() == model) {
-                        found = true;
-                        break;
-                    }
-                }
-                if (found) break;
-            }
-            if (!found) m_playSource->removeModel(model);
-        }
-    }
-
-    updateMenuStates();
-}
-
-void
-MainWindowBase::modelAdded(Model *model)
-{
-//    std::cerr << "MainWindowBase::modelAdded(" << model << ")" << std::endl;
-    m_playSource->addModel(model);
-}
-
-void
-MainWindowBase::mainModelChanged(WaveFileModel *model)
-{
-//    std::cerr << "MainWindowBase::mainModelChanged(" << model << ")" << std::endl;
-    updateDescriptionLabel();
-    if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
-    if (model && !m_playTarget && m_audioOutput) createPlayTarget();
-}
-
-void
-MainWindowBase::modelAboutToBeDeleted(Model *model)
-{
-//    std::cerr << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << std::endl;
-    if (model == m_viewManager->getPlaybackModel()) {
-        m_viewManager->setPlaybackModel(0);
-    }
-    m_playSource->removeModel(model);
-    FFTDataServer::modelAboutToBeDeleted(model);
-}
-
-void
-MainWindowBase::pollOSC()
-{
-    if (!m_oscQueue || m_oscQueue->isEmpty()) return;
-    std::cerr << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << std::endl;
-
-    if (m_openingAudioFile) return;
-
-    OSCMessage message = m_oscQueue->readMessage();
-
-    if (message.getTarget() != 0) {
-        return; //!!! for now -- this class is target 0, others not handled yet
-    }
-
-    handleOSCMessage(message);
-}
-
-void
-MainWindowBase::inProgressSelectionChanged()
-{
-    Pane *currentPane = 0;
-    if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) updateVisibleRangeDisplay(currentPane);
-}
-
-void
-MainWindowBase::contextHelpChanged(const QString &s)
-{
-    if (s == "" && m_myStatusMessage != "") {
-        statusBar()->showMessage(m_myStatusMessage);
-        return;
-    }
-    statusBar()->showMessage(s);
-}
-
-void
-MainWindowBase::openHelpUrl(QString url)
-{
-    // This method mostly lifted from Qt Assistant source code
-
-    QProcess *process = new QProcess(this);
-    connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
-
-    QStringList args;
-
-#ifdef Q_OS_MAC
-    args.append(url);
-    process->start("open", args);
-#else
-#ifdef Q_OS_WIN32
-
-	QString pf(getenv("ProgramFiles"));
-	QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE");
-
-	args.append(url);
-	process->start(command, args);
-
-#else
-#ifdef Q_WS_X11
-    if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
-        args.append("exec");
-        args.append(url);
-        process->start("kfmclient", args);
-    } else if (!qgetenv("BROWSER").isEmpty()) {
-        args.append(url);
-        process->start(qgetenv("BROWSER"), args);
-    } else {
-        args.append(url);
-        process->start("firefox", args);
-    }
-#endif
-#endif
-#endif
-}
-
--- a/main/MainWindowBase.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,339 +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-2007 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 _MAIN_WINDOW_BASE_H_
-#define _MAIN_WINDOW_BASE_H_
-
-#include <QFrame>
-#include <QString>
-#include <QUrl>
-#include <QMainWindow>
-#include <QPointer>
-
-#include "base/Command.h"
-#include "view/ViewManager.h"
-#include "base/PropertyContainer.h"
-#include "base/RecentFiles.h"
-#include "layer/LayerFactory.h"
-#include "transform/Transform.h"
-#include "document/SVFileReader.h"
-#include "data/fileio/FileFinder.h"
-#include "data/fileio/FileSource.h"
-#include <map>
-
-class Document;
-class PaneStack;
-class Pane;
-class View;
-class Fader;
-class Overview;
-class Layer;
-class WaveformLayer;
-class WaveFileModel;
-class AudioCallbackPlaySource;
-class AudioCallbackPlayTarget;
-class CommandHistory;
-class QMenu;
-class AudioDial;
-class QLabel;
-class QCheckBox;
-class PreferencesDialog;
-class QTreeView;
-class QPushButton;
-class OSCQueue;
-class OSCMessage;
-class KeyReference;
-class Labeller;
-
-/**
- * The base class for the SV main window.  This includes everything to
- * do with general document and pane stack management, but nothing
- * that involves user interaction -- this doesn't create the widget or
- * menu structures or editing tools, and if a function needs to open a
- * dialog, it shouldn't be in here.  This permits "variations on SV"
- * to use different subclasses retaining the same general structure.
- */
-
-class MainWindowBase : public QMainWindow
-{
-    Q_OBJECT
-
-public:
-    MainWindowBase(bool withAudioOutput, bool withOSCSupport);
-    virtual ~MainWindowBase();
-    
-    enum AudioFileOpenMode {
-        ReplaceMainModel,
-        CreateAdditionalModel,
-        ReplaceCurrentPane,
-        AskUser
-    };
-
-    enum FileOpenStatus {
-        FileOpenSucceeded,
-        FileOpenFailed,
-        FileOpenCancelled,
-        FileOpenWrongMode // attempted to open layer when no main model present
-    };
-
-    virtual FileOpenStatus open(QString fileOrUrl, AudioFileOpenMode = AskUser);
-    virtual FileOpenStatus open(FileSource source, AudioFileOpenMode = AskUser);
-    
-    virtual FileOpenStatus openAudio(FileSource source, AudioFileOpenMode = AskUser);
-    virtual FileOpenStatus openPlaylist(FileSource source, AudioFileOpenMode = AskUser);
-    virtual FileOpenStatus openLayer(FileSource source);
-    virtual FileOpenStatus openImage(FileSource source);
-
-    virtual FileOpenStatus openSessionFile(QString fileOrUrl);
-    virtual FileOpenStatus openSession(FileSource source);
-
-    virtual bool saveSessionFile(QString path);
-
-signals:
-    // Used to toggle the availability of menu actions
-    void canAddPane(bool);
-    void canDeleteCurrentPane(bool);
-    void canAddLayer(bool);
-    void canImportMoreAudio(bool);
-    void canImportLayer(bool);
-    void canExportAudio(bool);
-    void canExportLayer(bool);
-    void canExportImage(bool);
-    void canRenameLayer(bool);
-    void canEditLayer(bool);
-    void canMeasureLayer(bool);
-    void canSelect(bool);
-    void canClearSelection(bool);
-    void canEditSelection(bool);
-    void canDeleteSelection(bool);
-    void canPaste(bool);
-    void canInsertInstant(bool);
-    void canInsertInstantsAtBoundaries(bool);
-    void canRenumberInstants(bool);
-    void canDeleteCurrentLayer(bool);
-    void canZoom(bool);
-    void canScroll(bool);
-    void canPlay(bool);
-    void canFfwd(bool);
-    void canRewind(bool);
-    void canPlaySelection(bool);
-    void canSpeedUpPlayback(bool);
-    void canSlowDownPlayback(bool);
-    void canChangePlaybackSpeed(bool);
-    void canSave(bool);
-
-public slots:
-    virtual void preferenceChanged(PropertyContainer::PropertyName);
-
-protected slots:
-    virtual void zoomIn();
-    virtual void zoomOut();
-    virtual void zoomToFit();
-    virtual void zoomDefault();
-    virtual void scrollLeft();
-    virtual void scrollRight();
-    virtual void jumpLeft();
-    virtual void jumpRight();
-
-    virtual void showNoOverlays();
-    virtual void showMinimalOverlays();
-    virtual void showStandardOverlays();
-    virtual void showAllOverlays();
-
-    virtual void toggleZoomWheels();
-    virtual void togglePropertyBoxes();
-    virtual void toggleStatusBar();
-
-    virtual void play();
-    virtual void ffwd();
-    virtual void ffwdEnd();
-    virtual void rewind();
-    virtual void rewindStart();
-    virtual void stop();
-
-    virtual void deleteCurrentPane();
-    virtual void deleteCurrentLayer();
-
-    virtual void playLoopToggled();
-    virtual void playSelectionToggled();
-    virtual void playSoloToggled();
-
-    virtual void sampleRateMismatch(size_t, size_t, bool) = 0;
-    virtual void audioOverloadPluginDisabled() = 0;
-
-    virtual void playbackFrameChanged(unsigned long);
-    virtual void globalCentreFrameChanged(unsigned long);
-    virtual void viewCentreFrameChanged(View *, unsigned long);
-    virtual void viewZoomLevelChanged(View *, unsigned long, bool);
-    virtual void outputLevelsChanged(float, float) = 0;
-
-    virtual void currentPaneChanged(Pane *);
-    virtual void currentLayerChanged(Pane *, Layer *);
-
-    virtual void selectAll();
-    virtual void selectToStart();
-    virtual void selectToEnd();
-    virtual void selectVisible();
-    virtual void clearSelection();
-
-    virtual void cut();
-    virtual void copy();
-    virtual void paste();
-    virtual void deleteSelected();
-
-    virtual void insertInstant();
-    virtual void insertInstantAt(size_t);
-    virtual void insertInstantsAtBoundaries();
-    virtual void renumberInstants();
-
-    virtual void documentModified();
-    virtual void documentRestored();
-
-    virtual void layerAdded(Layer *);
-    virtual void layerRemoved(Layer *);
-    virtual void layerAboutToBeDeleted(Layer *);
-    virtual void layerInAView(Layer *, bool);
-
-    virtual void mainModelChanged(WaveFileModel *);
-    virtual void modelAdded(Model *);
-    virtual void modelAboutToBeDeleted(Model *);
-
-    virtual void updateMenuStates();
-    virtual void updateDescriptionLabel() = 0;
-
-    virtual void modelGenerationFailed(QString) = 0;
-    virtual void modelRegenerationFailed(QString, QString) = 0;
-
-    virtual void rightButtonMenuRequested(Pane *, QPoint point) = 0;
-
-    virtual void paneAdded(Pane *) = 0;
-    virtual void paneHidden(Pane *) = 0;
-    virtual void paneAboutToBeDeleted(Pane *) = 0;
-    virtual void paneDropAccepted(Pane *, QStringList) = 0;
-    virtual void paneDropAccepted(Pane *, QString) = 0;
-
-    virtual void pollOSC();
-    virtual void handleOSCMessage(const OSCMessage &) = 0;
-
-    virtual void contextHelpChanged(const QString &);
-    virtual void inProgressSelectionChanged();
-
-    virtual void closeSession() = 0;
-
-protected:
-    QString                  m_sessionFile;
-    QString                  m_audioFile;
-    Document                *m_document;
-
-    QLabel                  *m_descriptionLabel;
-    PaneStack               *m_paneStack;
-    ViewManager             *m_viewManager;
-    Layer                   *m_timeRulerLayer;
-
-    bool                     m_audioOutput;
-    AudioCallbackPlaySource *m_playSource;
-    AudioCallbackPlayTarget *m_playTarget;
-
-    OSCQueue                *m_oscQueue;
-
-    RecentFiles              m_recentFiles;
-    RecentFiles              m_recentTransforms;
-
-    bool                     m_documentModified;
-    bool                     m_openingAudioFile;
-    bool                     m_abandoning;
-
-    Labeller                *m_labeller;
-
-    int                      m_lastPlayStatusSec;
-    mutable QString          m_myStatusMessage;
-
-    bool                     m_initialDarkBackground;
-
-    WaveFileModel *getMainModel();
-    const WaveFileModel *getMainModel() const;
-    void createDocument();
-
-    Pane *addPaneToStack();
-    Layer *getSnapLayer() const;
-
-    class PaneCallback : public SVFileReaderPaneCallback
-    {
-    public:
-	PaneCallback(MainWindowBase *mw) : m_mw(mw) { }
-	virtual Pane *addPane() { return m_mw->addPaneToStack(); }
-	virtual void setWindowSize(int width, int height) {
-	    m_mw->resize(width, height);
-	}
-	virtual void addSelection(int start, int end) {
-	    m_mw->m_viewManager->addSelection(Selection(start, end));
-	}
-    protected:
-	MainWindowBase *m_mw;
-    };
-
-    class AddPaneCommand : public Command
-    {
-    public:
-	AddPaneCommand(MainWindowBase *mw);
-	virtual ~AddPaneCommand();
-	
-	virtual void execute();
-	virtual void unexecute();
-	virtual QString getName() const;
-
-	Pane *getPane() { return m_pane; }
-
-    protected:
-	MainWindowBase *m_mw;
-	Pane *m_pane; // Main window owns this, but I determine its lifespan
-	Pane *m_prevCurrentPane; // I don't own this
-	bool m_added;
-    };
-
-    class RemovePaneCommand : public Command
-    {
-    public:
-	RemovePaneCommand(MainWindowBase *mw, Pane *pane);
-	virtual ~RemovePaneCommand();
-	
-	virtual void execute();
-	virtual void unexecute();
-	virtual QString getName() const;
-
-    protected:
-	MainWindowBase *m_mw;
-	Pane *m_pane; // Main window owns this, but I determine its lifespan
-	Pane *m_prevCurrentPane; // I don't own this
-	bool m_added;
-    };
-
-    virtual bool checkSaveModified() = 0;
-
-    virtual QString getOpenFileName(FileFinder::FileType type);
-    virtual QString getSaveFileName(FileFinder::FileType type);
-    virtual void registerLastOpenedFilePath(FileFinder::FileType type, QString path);
-
-    virtual void createPlayTarget();
-    virtual void openHelpUrl(QString url);
-
-    virtual void setupMenus() = 0;
-    virtual void updateVisibleRangeDisplay(Pane *p) const = 0;
-
-    virtual void toXml(QTextStream &stream);
-};
-
-
-#endif
--- a/osc/OSCMessage.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +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 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.
-*/
-
-/*
-   This is a modified version of a source file from the 
-   Rosegarden MIDI and audio sequencer and notation editor.
-   This file copyright 2000-2006 Chris Cannam.
-*/
-
-#include "OSCMessage.h"
-
-
-OSCMessage::~OSCMessage()
-{
-    clearArgs();
-}
-
-void
-OSCMessage::clearArgs()
-{
-    m_args.clear();
-}
-
-void
-OSCMessage::addArg(QVariant arg)
-{
-    m_args.push_back(arg);
-}
-
-size_t
-OSCMessage::getArgCount() const
-{
-    return m_args.size();
-}
-
-const QVariant &
-OSCMessage::getArg(size_t i) const
-{
-    return m_args[i];
-}
-
--- a/osc/OSCMessage.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +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 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.
-*/
-
-/*
-   This is a modified version of a source file from the 
-   Rosegarden MIDI and audio sequencer and notation editor.
-   This file copyright 2000-2006 Chris Cannam.
-*/
-
-#ifndef _OSC_MESSAGE_H_
-#define _OSC_MESSAGE_H_
-
-#include <QString>
-#include <QVariant>
-
-#include <vector>
-#include <map>
-
-class OSCMessage
-{
-public:
-    OSCMessage() { }
-    ~OSCMessage();
-
-    void setTarget(const int &target) { m_target = target; }
-    int getTarget() const { return m_target; }
-
-    void setTargetData(const int &targetData) { m_targetData = targetData; }
-    int getTargetData() const { return m_targetData; }
-
-    void setMethod(QString method) { m_method = method; }
-    QString getMethod() const { return m_method; }
-
-    void clearArgs();
-    void addArg(QVariant arg);
-
-    size_t getArgCount() const;
-    const QVariant &getArg(size_t i) const;
-
-private:
-    int m_target;
-    int m_targetData;
-    QString m_method;
-    std::vector<QVariant> m_args;
-};
-
-#endif
--- a/osc/OSCQueue.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,222 +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 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.
-*/
-
-/*
-   This is a modified version of a source file from the 
-   Rosegarden MIDI and audio sequencer and notation editor.
-   This file copyright 2000-2006 Chris Cannam and QMUL.
-*/
-
-#include "OSCQueue.h"
-
-#include <iostream>
-
-#define OSC_MESSAGE_QUEUE_SIZE 1023
-
-#ifdef HAVE_LIBLO
-
-void
-OSCQueue::oscError(int num, const char *msg, const char *path)
-{
-    std::cerr << "ERROR: OSCQueue::oscError: liblo server error " << num
-	      << " in path " << path << ": " << msg << std::endl;
-}
-
-int
-OSCQueue::oscMessageHandler(const char *path, const char *types, lo_arg **argv,
-                            int argc, lo_message, void *user_data)
-{
-    OSCQueue *queue = static_cast<OSCQueue *>(user_data);
-
-    int target;
-    int targetData;
-    QString method;
-
-    if (!queue->parseOSCPath(path, target, targetData, method)) {
-	return 1;
-    }
-
-    OSCMessage message;
-    message.setTarget(target);
-    message.setTargetData(targetData);
-    message.setMethod(method);
-
-    int i = 0;
-
-    while (types && i < argc && types[i]) {
-
-        char type = types[i];
-        lo_arg *arg = argv[i];
-
-        switch (type) {
-        case 'i': message.addArg(arg->i); break;
-            // This conversion fails to compile in 64-bit environments
-            // at present, and we don't use the h type anyway so we
-            // can safely omit it
-//        case 'h': message.addArg(arg->h); break;
-        case 'f': message.addArg(arg->f); break;
-        case 'd': message.addArg(arg->d); break;
-        case 'c': message.addArg(arg->c); break;
-        case 't': message.addArg(arg->i); break;
-        case 's': message.addArg(&arg->s); break;
-        default:  std::cerr << "WARNING: OSCQueue::oscMessageHandler: "
-                            << "Unsupported OSC type '" << type << "'" 
-                            << std::endl;
-            break;
-        }
-
-	++i;
-    }
-
-    queue->postMessage(message);
-    return 0;
-}
-
-#endif
-   
-OSCQueue::OSCQueue() :
-#ifdef HAVE_LIBLO
-    m_thread(0),
-#endif
-    m_buffer(OSC_MESSAGE_QUEUE_SIZE)
-{
-#ifdef HAVE_LIBLO
-    m_thread = lo_server_thread_new(NULL, oscError);
-
-    lo_server_thread_add_method(m_thread, NULL, NULL,
-                                oscMessageHandler, this);
-
-    lo_server_thread_start(m_thread);
-
-    std::cout << "OSCQueue::OSCQueue: Base OSC URL is "
-              << lo_server_thread_get_url(m_thread) << std::endl;
-#endif
-}
-
-OSCQueue::~OSCQueue()
-{
-#ifdef HAVE_LIBLO
-    if (m_thread) {
-        lo_server_thread_stop(m_thread);
-    }
-#endif
-
-    while (m_buffer.getReadSpace() > 0) {
-        delete m_buffer.readOne();
-    }
-}
-
-bool
-OSCQueue::isOK() const
-{
-#ifdef HAVE_LIBLO
-    return (m_thread != 0);
-#else
-    return false;
-#endif
-}
-
-QString
-OSCQueue::getOSCURL() const
-{
-    QString url = "";
-#ifdef HAVE_LIBLO
-    url = lo_server_thread_get_url(m_thread);
-#endif
-    return url;
-}
-
-size_t
-OSCQueue::getMessagesAvailable() const
-{
-    return m_buffer.getReadSpace();
-}
-
-OSCMessage
-OSCQueue::readMessage()
-{
-    OSCMessage *message = m_buffer.readOne();
-    OSCMessage rmessage = *message;
-    delete message;
-    return rmessage;
-}
-
-void
-OSCQueue::postMessage(OSCMessage message)
-{
-    int count = 0, max = 5;
-    while (m_buffer.getWriteSpace() == 0) {
-        if (count == max) {
-            std::cerr << "ERROR: OSCQueue::postMessage: OSC message queue is full and not clearing -- abandoning incoming message" << std::endl;
-            return;
-        }
-        std::cerr << "WARNING: OSCQueue::postMessage: OSC message queue (capacity " << m_buffer.getSize() << " is full!" << std::endl;
-        std::cerr << "Waiting for something to be processed" << std::endl;
-#ifdef _WIN32
-        Sleep(1);
-#else
-        sleep(1);
-#endif
-        count++;
-    }
-
-    OSCMessage *mp = new OSCMessage(message);
-    m_buffer.write(&mp, 1);
-    std::cerr << "OSCQueue::postMessage: Posted OSC message: target "
-              << message.getTarget() << ", target data " << message.getTargetData()
-              << ", method " << message.getMethod().toStdString() << std::endl;
-    emit messagesAvailable();
-}
-
-bool
-OSCQueue::parseOSCPath(QString path, int &target, int &targetData,
-                       QString &method)
-{
-    while (path.startsWith("/")) {
-	path = path.right(path.length()-1);
-    }
-
-    int i = 0;
-
-    bool ok = false;
-    target = path.section('/', i, i).toInt(&ok);
-
-    if (!ok) {
-        target = 0;
-    } else {
-        ++i;
-        targetData = path.section('/', i, i).toInt(&ok);
-        if (!ok) {
-            targetData = 0;
-        } else {
-            ++i;
-        }
-    }
-
-    method = path.section('/', i, -1);
-
-    if (method.contains('/')) {
-        std::cerr << "ERROR: OSCQueue::parseOSCPath: malformed path \""
-                  << path.toStdString() << "\" (should be target/data/method or "
-                  << "target/method or method, where target and data "
-                  << "are numeric)" << std::endl;
-        return false;
-    }
-
-    std::cerr << "OSCQueue::parseOSCPath: good path \"" << path.toStdString()
-              << "\"" << std::endl;
-
-    return true;
-}
-
--- a/osc/OSCQueue.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +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 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.
-*/
-
-/*
-   This is a modified version of a source file from the 
-   Rosegarden MIDI and audio sequencer and notation editor.
-   This file copyright 2000-2006 Chris Cannam and QMUL.
-*/
-
-#ifndef _OSC_QUEUE_H_
-#define _OSC_QUEUE_H_
-
-#include "OSCMessage.h"
-
-#include "base/RingBuffer.h"
-
-#include <QObject>
-
-#ifdef HAVE_LIBLO
-#include <lo/lo.h>
-#endif
-
-class OSCQueue : public QObject
-{
-    Q_OBJECT
-
-public:
-    OSCQueue();
-    virtual ~OSCQueue();
-
-    bool isOK() const;
-
-    bool isEmpty() const { return getMessagesAvailable() == 0; }
-    size_t getMessagesAvailable() const;
-    OSCMessage readMessage();
-
-    QString getOSCURL() const;
-
-signals:
-    void messagesAvailable();
-
-protected:
-#ifdef HAVE_LIBLO
-    lo_server_thread m_thread;
-
-    static void oscError(int, const char *, const char *);
-    static int oscMessageHandler(const char *, const char *, lo_arg **,
-                                 int, lo_message, void *);
-#endif
-
-    void postMessage(OSCMessage);
-    bool parseOSCPath(QString path, int &target, int &targetData, QString &method);
-
-    RingBuffer<OSCMessage *> m_buffer;
-};
-
-#endif
-
--- a/osc/demoscript.sh	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,541 +0,0 @@
-#!/bin/bash
-
-audio=/data/music
-preferred=$audio/free
-list=audiofiles.txt
-used=audiofiles-used.txt
-
-df=vamp:vamp-aubio:aubioonset:detectionfunction
-#df=vamp:qm-vamp-plugins:qm-tempotracker:detection_fn
-onsets=vamp:vamp-aubio:aubioonset:onsets
-#onsets=vamp:qm-vamp-plugins:qm-tempotracker:beats
-beats=vamp:vamp-aubio:aubiotempo:beats
-#beats=$onsets
-#onsets=$beats
-chromagram=vamp:qm-vamp-plugins:qm-chromagram:chromagram
-notes=vamp:vamp-aubio:aubionotes:notes
-
-pid=`cat /tmp/demoscript.pid 2>/dev/null`
-if [ -n "$pid" ]; then
-    kill "$pid"
-fi
-echo $$ > /tmp/demoscript.pid
-trap "rm /tmp/demoscript.pid" 0
-
-sv-command quit
-sleep 1
-killall -9 sonic-visualiser
-sleep 1
-
-pick_file()
-{
-    file=""
-    count=`wc -l "$list" 2>/dev/null | awk '{ print $1 }'`
-    if [ ! -f "$list" ] || [ "$count" -eq "0" ] ; then
-	find "$audio" -name \*.ogg -print >> "$list"
-	find "$audio" -name \*.mp3 -print >> "$list"
-	find "$audio" -name \*.wav -print >> "$list"
-	find "$preferred" -name \*.ogg -print >> "$list"
-	find "$preferred" -name \*.mp3 -print >> "$list"
-	find "$preferred" -name \*.wav -print >> "$list"
-	count=`wc -l "$list" 2>/dev/null | awk '{ print $1 }'`
-    fi
-    while [ -z "$file" ]; do
-	index=$((RANDOM % $count))
-	file=`tail +"$index" "$list" | head -1`
-	[ -f "$file" ] || continue
-    done
-    fgrep -v "$file" "$list" > "$list"_ && mv "$list"_ "$list"
-    echo "$file"
-}
-
-load_a_file()
-{
-    file=`pick_file`
-    if ! sv-command open "$file"; then
-	pid="`pidof sonic-visualiser`"
-	if [ -z "$pid" ]; then
-	    ( setsid sonic-visualiser -geometry 1000x500+10+100 & )
-	    sleep 2
-            sudo renice +19 `pidof sonic-visualiser`
-            sudo renice +18 `pidof Xorg`
-            sv-command resize 1000 500
-	    load_a_file
-	else
-	    echo "ERROR: Unable to contact sonic-visualiser pid $pid" 1>&2
-	    exit 1
-	fi
-    fi
-}
-
-show_stuff()
-{
-    sv-command set overlays 2
-#    sv-command set zoomwheels 1
-    sv-command set propertyboxes 1
-}
-
-hide_stuff()
-{
-    sv-command set overlays 0
-#    sv-command set zoomwheels 0
-    sv-command set propertyboxes 0
-}
-
-reset()
-{
-    for pane in 1 2 3 4 5; do
-	for layer in 1 2 3 4 5 6 7 8 9 10; do
-	    sv-command delete layer
-	done
-	sv-command delete pane
-    done
-    sv-command zoom default
-    sv-command add waveform
-    show_stuff
-}
-
-scroll_and_zoom()
-{
-    sv-command set overlays 0
-    sv-command set zoomwheels 0
-    sv-command set propertyboxes 0
-#    sv-command setcurrent 1 1
-#    sv-command delete layer
-#    sv-command setcurrent 1 1
-    sv-command set layer Colour Red
-    sleep 1
-    sv-command set pane Global-Zoom off
-    sv-command set pane Global-Scroll off
-    sv-command set pane Follow-Playback Scroll
-    for zoom in 950 900 850 800 750 700 650 600 550 512 450 400 350 300 256 192 160 128 96 64 48 32 24 16; do
-	sv-command zoom $zoom
-	sleep 0.1
-    done
-}
-
-play()
-{
-    sv-command play "$@"
-}
-
-fade_in()
-{
-    sv-command set gain 0
-    sleep 0.5
-    play "$@"
-    for gain in 0.001 0.01 0.05 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1; do
-	sv-command set gain $gain
-	sleep 0.1
-    done
-}
-
-fade_out()
-{
-    for gain in 0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2 0.1 0.05 0.01 0.001; do
-	sv-command set gain $gain
-	sleep 0.1
-    done
-    stop
-    sv-command set gain 1
-}
-
-slow()
-{
-#    for speed in -1 -10 -20 -30 -40 -50 -60 -70 -80 -100 -140 -200 -250 -300 -400 -500 -700 -800 -900 -1000; do
-#	sv-command set speedup "$speed"
-#	sleep 1
-#    done
-    for speed in -20 -100 -1000; do
-        sv-command set speedup "$speed"
-        sleep 10
-    done
-}
-
-stop()
-{
-    sv-command stop "$@"
-    sv-command set speedup 0
-}
-
-quit()
-{
-    sv-command quit
-}
-
-add_melodic_range_spectrogram()
-{
-    sv-command set propertyboxes 1
-    sv-command add spectrogram
-    sv-command set layer Window-Size 8192
-#    sv-command set layer Window-Size 4096
-    sv-command set layer Window-Overlap 4
-#    sv-command set layer Window-Overlap 3
-    sv-command set layer Frequency-Scale Log
-    sv-command set layer Colour-Scale Meter
-}
-
-zoom_in_spectrogram() 
-{
-    sv-command zoomvertical 43 8000
-    for x in 1 2 3 4 5 6; do
-	max=$((8000 - 1000*$x))
-	sv-command zoomvertical 43 "$max"
-	sleep 0.5
-    done
-    for x in 1 2 3 4 5; do
-	max=$((2000 - 100 * $x))
-	sv-command zoomvertical 43 "$max"
-	sleep 0.5
-    done
-}
-
-zoom_in_spectrogram_further() 
-{
-    for x in 1 2 3 4 5; do
-	sv-command zoomvertical in
-    done
-}
-
-playback_bits()
-{
-    sv-command setcurrent 1
-    sv-command set pane Global-Zoom off
-    sv-command set pane Global-Scroll off
-    sv-command set pane Follow-Playback Scroll
-    sv-command jump 10
-    sv-command setcurrent 1 1
-    sv-command delete layer
-    sv-command setcurrent 1 1
-#    sv-command setcurrent 1 2
-    sv-command set layer Colour Blue
-    sleep 5
-    hide_stuff
-    sv-command set overlays 0
-    sv-command set zoomwheels 0
-    sv-command set propertyboxes 0
-    fade_in
-    sleep 10
-#    sv-command set layer Colour Blue
-#    sleep 1
-#    sv-command set layer Colour Orange
-#    sleep 1
-#    sv-command set layer Colour Red
-#    sleep 1
-#    sv-command set layer Colour Green
-#    sleep 1
-#    sleep 1
-    
-    
-#    scroll_and_zoom
-
-#    sv-command set overlays 0
-#    sv-command set zoomwheels 0
-#    sv-command set propertyboxes 0
-#    sv-command setcurrent 1 1
-#    sv-command delete layer
-#    sv-command setcurrent 1 1
-#    sv-command set layer Colour Red
-#    sleep 1
-#    sv-command set pane Global-Zoom off
-#    sv-command set pane Global-Scroll off
-#    sv-command set pane Follow-Playback Scroll
-    sv-command set zoomwheels 1
-    sleep 1
-    for zoom in 950 900 850 800 750 700 650 600 550 512 450 400 350 300 256 192 160 128 96 64 48 32 24 16; do
-	sv-command zoom $zoom
-	sleep 0.1
-    done
-    
-    sleep 1
-    sv-command set zoomwheels 0
-    sv-command zoom 16
-
-    sleep 10
-    #slow
-    #sv-command set layer Normalize-Visible-Area on
-#    for zoom in 15 14 13 12 11 10 9 8 7 6 5 4 ; do
-#	sv-command zoom $zoom
-#	sleep 0.1
- #   done
-    sleep 1
-    sv-command set zoomwheels 0
-    slow
-    sleep 7
-    fade_out
-    sv-command setcurrent 1
-    sv-command set pane Follow-Playback Page
-    sv-command set pane Global-Zoom on
-    sv-command set pane Global-Scroll on
-    done_playback_bits=1
-}
-
-spectrogram_bits()
-{
-    sv-command set pane Global-Zoom on
-    sv-command zoom 1024
-    add_melodic_range_spectrogram
-    sv-command zoom 1024
-    sleep 5
-    sv-command jump 10
-    sleep 20
-    zoom_in_spectrogram
-    sleep 20
-
-    sv-command select 7.5 11
-    fade_in selection
-    sleep 10
-    sv-command set speedup -200
-    sleep 10
-    sv-command setcurrent 1
-    sv-command delete pane
-    sv-command zoom in
-    sv-command setcurrent 1 2
-    sv-command set layer Normalize-Columns off
-    sv-command set layer Normalize-Visible-Area on
-    sleep 20
-    sv-command set speedup 0
-    sleep 10
-    sv-command select none
-#    fade_out
-
-#    if [ -n "$done_playback_bits" ]; then
-#	sv-command setcurrent 1
-#	sv-command zoom out
-#	sv-command zoom outvamp:qm-vamp-plugins:qm-chromagram:chromagram
-#	sv-command zoom out
-#	sv-command zoom out
-#	sv-command zoom out
-#	sv-command setcurrent 2
-#    fi
-    
-#    hide_stuff
-#    fade_in
-    sleep 10
-#    sv-command set layer Bin-Display Frequencies
-#    sv-command set layer Normalize-Columns on
-#    sleep 20
-    sv-command set layer Bin-Display "All Bins"
-    sv-command set layer Normalize-Columns on
-    sv-command set layer Normalize-Visible-Area off
-    sv-command set layer Colour-Scale 0
-    sv-command set layer Colour "Red on Blue"
-    sv-command zoomvertical 23 800
-    sleep 20
-    sv-command transform $onsets
-    sv-command set layer Colour Orange
-    sleep 20
-    fade_out
-    sleep 1
-#    sv-command jump 10
-#    sv-command setcurrent 1 2
-#    sv-command set layer Colour "Black on White"
-#    sv-command transform $notes
-#    sv-command set layer Colour Orange
-    sleep 10
-#    sv-command setcurrent 1 3
-#    sv-command delete layer
-    sv-command setcurrent 1 3
-    sv-command delete layer
-    sv-command setcurrent 1 2
-    sv-command set layer Colour Default
-    done_spectrogram_bits=1
-
-#    zoom_in_spectrogram_further
-}
-
-onset_bits()
-{
-    show_stuff
-    sv-command set zoomwheels 0
-    sv-command setcurrent 1
-    sv-command set pane Global-Zoom on
-    sv-command set pane Global-Scroll on
-    sleep 0.5
-    sv-command set layer Colour Blue
-    sleep 0.5
-    sv-command set layer Colour Orange
-    sleep 0.5
-    sv-command set layer Colour Red
-    sleep 0.5
-    sv-command set layer Colour Green
-    sleep 1
-#    sleep 1
-#    if [ -n "$done_spectrogram_bits" ]; then
-#	sv-command setcurrent 2
-#	sv-command delete pane
-#    fi
-#    sv-command zoom default
-#    sv-command zoom in
-#    sv-command zoom in
-#    sv-command zoom in
-    sv-command zoom 192
-    sv-command zoom in
-    sv-command add timeruler
-    sv-command jump 0
-    sv-command transform $df
-    sv-command set layer Colour Black
-    sleep 5
-    sv-command set layer Plot-Type Curve
-    sleep 5
-    sv-command jump 30
-    sv-command setcurrent 1
-    sv-command set pane Follow-Playback Page
-    sv-command transform $df
-    sv-command set layer Colour Red
-    sleep 5
-    sv-command jump 30
-    sleep 5
-    if [ "$RANDOM" -lt 16384 ]; then
-        sv-command set layer Vertical-Scale "Log Scale"
-    fi
-    sv-command set layer Plot-Type Segmentation
-    sleep 5 
-#    hide_stuff
-    sleep 10
-    sv-command set overlays 0
-    sv-command set propertyboxes 0
-#    sv-command setcurrent 1 1
-#    sv-command set layer Colour Black
-#    sv-command setcurrent 1 2
-    sleep 2
-    fade_in
-    sleep 2
-    sv-command transform $onsets
-    sv-command set layer Colour Black
-    sv-command setcurrent 2
-    sv-command transform $onsets
-    sv-command set layer Colour Blue
-    sleep 20
-#    sv-command setcurrent 2
-#    sv-command transform vamp:qm-vamp-plugins:qm-tempotracker:beats
-#    sv-command transform $beats
-    sleep 20
-#    fade_out
-#    show_stuff
-}
-
-selection_bits()
-{
-#    reset
-    sv-command set overlays 1
-    sv-command set zoomwheels 0
-    sv-command resize 1000 500
-    sv-command zoom default
-    sv-command setcurrent 2
-    sv-command delete pane
-#    if [ -n "$done_playback_bits" ]; then
-	sv-command setcurrent 1 2
-#    else
-#	sv-command setcurrent 1 3
-#    fi
-    sv-command delete layer
-#    if [ -n "$done_playback_bits" ]; then
-	sv-command setcurrent 1 2
-#    else
-#	sv-command setcurrent 1 3
-#    fi
-    sv-command delete layer
-    sv-command setcurrent 1 2
-    sv-command set layer Colour Orange
-#    sv-command transform vamp:qm-vamp-plugins:qm-tempotracker:beats
-    sv-command transform $beats
-#    sv-command setcurrent 1 2
-    sv-command set layer Colour Black
-    sleep 20
-    sv-command loop on
-    base=$((RANDOM % 100))
-    sv-command select $base $base.3
-#    fade_in selection
-    play selection
-    sleep 8
-    base=$((base + 4))
-    sv-command addselect $base $base.1
-    #sleep 12
-    base=$((base + 2))
-    sv-command addselect $base $base.1
-    #sleep 6
-    base=$((base + 2))
-    sv-command addselect $base $base.3
-    #sleep 6
-    base=$((base + 3))
-    sv-command addselect $base $base.3
-    #sleep 6
-    base=$((base + 2))
-    sv-command addselect $base $base.3
-    sleep 4
-    sv-command delete layer
-    sleep 16
-    sv-command set speedup -50
-    sleep 14
-    sv-command set speedup 50
-    sleep 8
-    sv-command set speedup 100
-    sleep 5
-    sv-command set speedup 200
-    fade_out
-#    sleep 10
-    sv-command select none
-    sv-command set overlays 2
-    sv-command set propertyboxes 1
-#    sv-command setcurrent 1 3
-#    sv-command delete layer
-    sv-command setcurrent 1 2
-    sv-command set layer Colour Black
-}
-
-chromagram_bits()
-{
-#    add_melodic_range_spectrogram
-#    sleep 10
-    sv-command add timeruler
-    sleep 5
-    sv-command jump 10
-    sv-command zoom out
-    sleep 5
-    sv-command transform $chromagram
-    sleep 40
-    sv-command zoom out
-    fade_in
-    sleep 20
-    fade_out
-}
-
-while /bin/true; do
-
-sleep 2
-load_a_file
-sv-command loop on
-
-sv-command resize 1000 500
-show_stuff
-sleep 5
-sleep 20
-playback_bits
-
-#sleep 10
-sv-command resize 1000 700
-sv-command zoom default
-show_stuff
-onset_bits
-
-selection_bits
-
-#sv-command resize 1000 700
-
-#sleep 10
-sv-command resize 1000 700
-#show_stuff
-spectrogram_bits
-
-#sleep 10
-#sv-command jump 0
-#show_stuff
-#chromagram_bits
-
-sleep 20
-
-#reset
-killall -9 sonic-visualiser
-
-done
--- a/osc/sv-command	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,55 +0,0 @@
-#!/bin/sh
-#
-# A very simple command shell for Sonic Visualiser.
-# 
-# This provides a wrapper for the sv-osc-send program, which is a
-# generic OSC sending program (not specific to SV, despite its name).
-# This script attempts to guess the OSC port number for an SV
-# process running on the local host, and then composes a method name
-# and arguments into a complete OSC call.
-# 
-# You can either run this with the method and its arguments on the
-# command line, e.g. "sv-command set layer Frequency-Scale Log", or
-# you can provide a series of method + argument commands on stdin.
-# 
-# Unless you use the -q option, this script will echo the OSC URL
-# and arguments that it is sending for each command.
-#
-# Note that the method and arguments may not contain spaces.
-# 
-# Chris Cannam, Nov 2006
-
-quiet=
-if [ "$1" = "-q" ]; then
-    quiet=true; shift;
-fi
-
-# The yucky bit
-
-port=`lsof -c sonic- | \
-          grep UDP | \
-          sed -e 's/^.*[^0-9]\([0-9][0-9]*\) *$/\1/' | \
-          grep -v ' ' | \
-          head -1 `
-
-host=127.0.0.1
-scheme=osc.udp
-
-if [ -z "$port" ]; then
-    echo "Sonic Visualiser OSC port not found"
-    exit 1
-fi
-
-if [ -n "$1" ]; then
-    command=$1; shift
-    [ -z "$quiet" ] && echo "$scheme://$host:$port/$command" "$@"
-    sv-osc-send "$scheme://$host:$port/$command" "$@"
-else
-    while read command a1 a2 a3 a4 a5; do
-        [ -z "$command" ] && continue
-	[ -z "$quiet" ] && echo "$scheme://$host:$port/$command" $a1 $a2 $a3 $a4 $a5
-	sv-osc-send "$scheme://$host:$port/$command" $a1 $a2 $a3 $a4 $a5
-    done
-fi
-
-exit 0
--- a/osc/sv-osc-send.c	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,73 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <ctype.h>
-#include <lo/lo.h>
-
-void
-usage(char *program_name)
-{
-    char *base_name = strrchr(program_name, '/');
-    
-    if (base_name && *(base_name + 1) != 0) {
-        base_name += 1;
-    } else {
-        base_name = program_name;
-    }
-
-    fprintf(stderr, "\nusage: %s <OSC URL> [<values>]\n\n", program_name);
-    fprintf(stderr, "example OSC URLs:\n\n"
-                    "  osc.udp://localhost:19383/path/test 1.0 4.2\n"
-                    "  osc.udp://my.host.org:10886/3/13/load file\n\n");
-    fprintf(stderr, "numeric arguments will be treated as OSC 'f' floating point types.\n\n");
-    exit(1);
-}
-
-int main(int argc, char *argv[])
-{
-    lo_address a;
-    char *url, *host, *port, *path;
-    lo_message message;
-    unsigned int i;
-
-    if (argc < 2) {
-        usage(argv[0]);
-        /* does not return */
-    }
-    url = argv[1];
-
-    host = lo_url_get_hostname(url);
-    port = lo_url_get_port(url);
-    path = lo_url_get_path(url);
-    a = lo_address_new(host, port);
-
-    message = lo_message_new();
-
-    for (i = 0; i + 2 < argc; ++i) {
-
-	int index = i + 2;
-	char *param;
-
-	param = argv[index];
-	if (!isdigit(param[0])) {
-	    lo_message_add_string(message, argv[index]);
-	} else {
-	    lo_message_add_float(message, atof(argv[index]));
-	}
-    }
-
-    lo_send_message(a, path, message);
-
-    if (lo_address_errno(a)) {
-	printf("liblo error: %s\n", lo_address_errstr(a));
-    }
-
-    free(host);
-    free(port);
-    free(path);
-
-    return 0;
-}
-
--- a/sv.pro	Wed Oct 24 16:00:30 2007 +0000
+++ b/sv.pro	Wed Oct 24 16:34:31 2007 +0000
@@ -11,69 +11,33 @@
 
 ICON = icons/sv-macicon.icns
 
-DEPENDPATH += . .. audioio document i18n main osc transform
-INCLUDEPATH += . .. audioio document transform osc main
-LIBPATH = ../view ../layer ../data ../widgets ../plugin ../base ../system $$LIBPATH
+DEPENDPATH += . .. i18n main transform
+INCLUDEPATH += . .. transform main
+LIBPATH = ../view ../layer ../data ../widgets ../plugin ../base ../system ../document ../audioio $$LIBPATH
 
 contains(DEFINES, BUILD_STATIC):LIBS -= -ljack
 
-LIBS = -lsvview -lsvlayer -lsvdata -lsvwidgets -lsvplugin -lsvbase -lsvsystem $$LIBS
+LIBS = -lsvdocument -lsvaudioio -lsvview -lsvlayer -lsvdata -lsvwidgets -lsvplugin -lsvbase -lsvsystem $$LIBS
 
 PRE_TARGETDEPS += ../view/libsvview.a \
                   ../layer/libsvlayer.a \
                   ../data/libsvdata.a \
+                  ../document/libsvdocument.a \
                   ../widgets/libsvwidgets.a \
                   ../plugin/libsvplugin.a \
                   ../base/libsvbase.a \
+                  ../audioio/libsvaudioio.a \
                   ../system/libsvsystem.a
 
 OBJECTS_DIR = tmp_obj
 MOC_DIR = tmp_moc
 
 # Input
-HEADERS += audioio/AudioCallbackPlaySource.h \
-           audioio/AudioCallbackPlayTarget.h \
-           audioio/AudioCoreAudioTarget.h \
-           audioio/AudioGenerator.h \
-           audioio/AudioJACKTarget.h \
-           audioio/AudioPortAudioTarget.h \
-           audioio/AudioTargetFactory.h \
-           audioio/PhaseVocoderTimeStretcher.h \
-           audioio/PlaySpeedRangeMapper.h \
-           document/Document.h \
-           document/SVFileReader.h \
-           main/MainWindow.h \
-           main/MainWindowBase.h \
-           main/PreferencesDialog.h \
-           osc/OSCMessage.h \
-           osc/OSCQueue.h \
-           transform/FeatureExtractionPluginTransform.h \
-           transform/PluginTransform.h \
-           transform/RealTimePluginTransform.h \
-           transform/Transform.h \
-           transform/TransformFactory.h
-SOURCES += audioio/AudioCallbackPlaySource.cpp \
-           audioio/AudioCallbackPlayTarget.cpp \
-           audioio/AudioCoreAudioTarget.cpp \
-           audioio/AudioGenerator.cpp \
-           audioio/AudioJACKTarget.cpp \
-           audioio/AudioPortAudioTarget.cpp \
-           audioio/AudioTargetFactory.cpp \
-           audioio/PhaseVocoderTimeStretcher.cpp \
-           audioio/PlaySpeedRangeMapper.cpp \
-           document/Document.cpp \
-           document/SVFileReader.cpp \
-           main/main.cpp \
+HEADERS += main/MainWindow.h \
+           main/PreferencesDialog.h
+SOURCES += main/main.cpp \
            main/MainWindow.cpp \
-           main/MainWindowBase.cpp \
-           main/PreferencesDialog.cpp \
-           osc/OSCMessage.cpp \
-           osc/OSCQueue.cpp \
-           transform/FeatureExtractionPluginTransform.cpp \
-           transform/PluginTransform.cpp \
-           transform/RealTimePluginTransform.cpp \
-           transform/Transform.cpp \
-           transform/TransformFactory.cpp
+           main/PreferencesDialog.cpp
 RESOURCES += sonic-visualiser.qrc
 
 
--- a/transform/FeatureExtractionPluginTransform.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,553 +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 "FeatureExtractionPluginTransform.h"
-
-#include "plugin/FeatureExtractionPluginFactory.h"
-#include "plugin/PluginXml.h"
-#include "vamp-sdk/Plugin.h"
-
-#include "data/model/Model.h"
-#include "base/Window.h"
-#include "data/model/SparseOneDimensionalModel.h"
-#include "data/model/SparseTimeValueModel.h"
-#include "data/model/EditableDenseThreeDimensionalModel.h"
-#include "data/model/DenseTimeValueModel.h"
-#include "data/model/NoteModel.h"
-#include "data/model/FFTModel.h"
-#include "data/model/WaveFileModel.h"
-
-#include <QMessageBox>
-
-#include <iostream>
-
-FeatureExtractionPluginTransform::FeatureExtractionPluginTransform(Model *inputModel,
-								   QString pluginId,
-                                                                   const ExecutionContext &context,
-                                                                   QString configurationXml,
-								   QString outputName) :
-    PluginTransform(inputModel, context),
-    m_plugin(0),
-    m_descriptor(0),
-    m_outputFeatureNo(0)
-{
-//    std::cerr << "FeatureExtractionPluginTransform::FeatureExtractionPluginTransform: plugin " << pluginId.toStdString() << ", outputName " << outputName.toStdString() << std::endl;
-
-    FeatureExtractionPluginFactory *factory =
-	FeatureExtractionPluginFactory::instanceFor(pluginId);
-
-    if (!factory) {
-	std::cerr << "FeatureExtractionPluginTransform: No factory available for plugin id \""
-		  << pluginId.toStdString() << "\"" << std::endl;
-	return;
-    }
-
-    m_plugin = factory->instantiatePlugin(pluginId, m_input->getSampleRate());
-
-    if (!m_plugin) {
-	std::cerr << "FeatureExtractionPluginTransform: Failed to instantiate plugin \""
-		  << pluginId.toStdString() << "\"" << std::endl;
-	return;
-    }
-
-    if (configurationXml != "") {
-        PluginXml(m_plugin).setParametersFromXml(configurationXml);
-    }
-
-    DenseTimeValueModel *input = getInput();
-    if (!input) return;
-
-    size_t channelCount = input->getChannelCount();
-    if (m_plugin->getMaxChannelCount() < channelCount) {
-	channelCount = 1;
-    }
-    if (m_plugin->getMinChannelCount() > channelCount) {
-	std::cerr << "FeatureExtractionPluginTransform:: "
-		  << "Can't provide enough channels to plugin (plugin min "
-		  << m_plugin->getMinChannelCount() << ", max "
-		  << m_plugin->getMaxChannelCount() << ", input model has "
-		  << input->getChannelCount() << ")" << std::endl;
-	return;
-    }
-
-    std::cerr << "Initialising feature extraction plugin with channels = "
-              << channelCount << ", step = " << m_context.stepSize
-              << ", block = " << m_context.blockSize << std::endl;
-
-    if (!m_plugin->initialise(channelCount,
-                              m_context.stepSize,
-                              m_context.blockSize)) {
-        std::cerr << "FeatureExtractionPluginTransform: Plugin "
-                  << m_plugin->getIdentifier() << " failed to initialise!" << std::endl;
-        return;
-    }
-
-    Vamp::Plugin::OutputList outputs = m_plugin->getOutputDescriptors();
-
-    if (outputs.empty()) {
-	std::cerr << "FeatureExtractionPluginTransform: Plugin \""
-		  << pluginId.toStdString() << "\" has no outputs" << std::endl;
-	return;
-    }
-    
-    for (size_t i = 0; i < outputs.size(); ++i) {
-	if (outputName == "" || outputs[i].identifier == outputName.toStdString()) {
-	    m_outputFeatureNo = i;
-	    m_descriptor = new Vamp::Plugin::OutputDescriptor
-		(outputs[i]);
-	    break;
-	}
-    }
-
-    if (!m_descriptor) {
-	std::cerr << "FeatureExtractionPluginTransform: Plugin \""
-		  << pluginId.toStdString() << "\" has no output named \""
-		  << outputName.toStdString() << "\"" << std::endl;
-	return;
-    }
-
-//    std::cerr << "FeatureExtractionPluginTransform: output sample type "
-//	      << m_descriptor->sampleType << std::endl;
-
-    int binCount = 1;
-    float minValue = 0.0, maxValue = 0.0;
-    bool haveExtents = false;
-    
-    if (m_descriptor->hasFixedBinCount) {
-	binCount = m_descriptor->binCount;
-    }
-
-//    std::cerr << "FeatureExtractionPluginTransform: output bin count "
-//	      << binCount << std::endl;
-
-    if (binCount > 0 && m_descriptor->hasKnownExtents) {
-	minValue = m_descriptor->minValue;
-	maxValue = m_descriptor->maxValue;
-        haveExtents = true;
-    }
-
-    size_t modelRate = m_input->getSampleRate();
-    size_t modelResolution = 1;
-    
-    switch (m_descriptor->sampleType) {
-
-    case Vamp::Plugin::OutputDescriptor::VariableSampleRate:
-	if (m_descriptor->sampleRate != 0.0) {
-	    modelResolution = size_t(modelRate / m_descriptor->sampleRate + 0.001);
-	}
-	break;
-
-    case Vamp::Plugin::OutputDescriptor::OneSamplePerStep:
-	modelResolution = m_context.stepSize;
-	break;
-
-    case Vamp::Plugin::OutputDescriptor::FixedSampleRate:
-	modelRate = size_t(m_descriptor->sampleRate + 0.001);
-	break;
-    }
-
-    if (binCount == 0) {
-
-	m_output = new SparseOneDimensionalModel(modelRate, modelResolution,
-						 false);
-
-    } else if (binCount == 1) {
-
-        SparseTimeValueModel *model;
-        if (haveExtents) {
-            model = new SparseTimeValueModel
-                (modelRate, modelResolution, minValue, maxValue, false);
-        } else {
-            model = new SparseTimeValueModel
-                (modelRate, modelResolution, false);
-        }
-        model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str());
-
-        m_output = model;
-
-    } else if (m_descriptor->sampleType ==
-	       Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
-
-        // We don't have a sparse 3D model, so interpret this as a
-        // note model.  There's nothing to define which values to use
-        // as which parameters of the note -- for the moment let's
-        // treat the first as pitch, second as duration in frames,
-        // third (if present) as velocity. (Our note model doesn't
-        // yet store velocity.)
-        //!!! todo: ask the user!
-	
-        NoteModel *model;
-        if (haveExtents) {
-            model = new NoteModel
-                (modelRate, modelResolution, minValue, maxValue, false);
-        } else {
-            model = new NoteModel
-                (modelRate, modelResolution, false);
-        }            
-        model->setScaleUnits(outputs[m_outputFeatureNo].unit.c_str());
-
-        m_output = model;
-
-    } else {
-
-        EditableDenseThreeDimensionalModel *model =
-            new EditableDenseThreeDimensionalModel
-            (modelRate, modelResolution, binCount, false);
-
-	if (!m_descriptor->binNames.empty()) {
-	    std::vector<QString> names;
-	    for (size_t i = 0; i < m_descriptor->binNames.size(); ++i) {
-		names.push_back(m_descriptor->binNames[i].c_str());
-	    }
-	    model->setBinNames(names);
-	}
-        
-        m_output = model;
-    }
-}
-
-FeatureExtractionPluginTransform::~FeatureExtractionPluginTransform()
-{
-    std::cerr << "FeatureExtractionPluginTransform::~FeatureExtractionPluginTransform()" << std::endl;
-    delete m_plugin;
-    delete m_descriptor;
-}
-
-DenseTimeValueModel *
-FeatureExtractionPluginTransform::getInput()
-{
-    DenseTimeValueModel *dtvm =
-	dynamic_cast<DenseTimeValueModel *>(getInputModel());
-    if (!dtvm) {
-	std::cerr << "FeatureExtractionPluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
-    }
-    return dtvm;
-}
-
-void
-FeatureExtractionPluginTransform::run()
-{
-    DenseTimeValueModel *input = getInput();
-    if (!input) return;
-
-    if (!m_output) return;
-
-    while (!input->isReady()) {
-/*
-        if (dynamic_cast<WaveFileModel *>(input)) {
-            std::cerr << "FeatureExtractionPluginTransform::run: Model is not ready, but it's not a WaveFileModel (it's a " << typeid(input).name() << "), so that's OK" << std::endl;
-            sleep(2);
-            break; // no need to wait
-        }
-*/
-        std::cerr << "FeatureExtractionPluginTransform::run: Waiting for input model to be ready..." << std::endl;
-        sleep(1);
-    }
-
-    size_t sampleRate = m_input->getSampleRate();
-
-    size_t channelCount = input->getChannelCount();
-    if (m_plugin->getMaxChannelCount() < channelCount) {
-	channelCount = 1;
-    }
-
-    float **buffers = new float*[channelCount];
-    for (size_t ch = 0; ch < channelCount; ++ch) {
-	buffers[ch] = new float[m_context.blockSize + 2];
-    }
-
-    bool frequencyDomain = (m_plugin->getInputDomain() ==
-                            Vamp::Plugin::FrequencyDomain);
-    std::vector<FFTModel *> fftModels;
-
-    if (frequencyDomain) {
-        for (size_t ch = 0; ch < channelCount; ++ch) {
-            FFTModel *model = new FFTModel
-                                  (getInput(),
-                                   channelCount == 1 ? m_context.channel : ch,
-                                   m_context.windowType,
-                                   m_context.blockSize,
-                                   m_context.stepSize,
-                                   m_context.blockSize,
-                                   false);
-            if (!model->isOK()) {
-                QMessageBox::critical
-                    (0, tr("FFT cache failed"),
-                     tr("Failed to create the FFT model for this transform.\n"
-                        "There may be insufficient memory or disc space to continue."));
-                delete model;
-                setCompletion(100);
-                return;
-            }
-            model->resume();
-            fftModels.push_back(model);
-        }
-    }
-
-    long startFrame = m_input->getStartFrame();
-    long   endFrame = m_input->getEndFrame();
-
-    long contextStart = m_context.startFrame;
-    long contextDuration = m_context.duration;
-
-    if (contextStart == 0 || contextStart < startFrame) {
-        contextStart = startFrame;
-    }
-
-    if (contextDuration == 0) {
-        contextDuration = endFrame - contextStart;
-    }
-    if (contextStart + contextDuration > endFrame) {
-        contextDuration = endFrame - contextStart;
-    }
-
-    long blockFrame = contextStart;
-
-    long prevCompletion = 0;
-
-    setCompletion(0);
-
-    while (!m_abandoned) {
-
-        if (frequencyDomain) {
-            if (blockFrame - int(m_context.blockSize)/2 >
-                contextStart + contextDuration) break;
-        } else {
-            if (blockFrame >= 
-                contextStart + contextDuration) break;
-        }
-
-//	std::cerr << "FeatureExtractionPluginTransform::run: blockFrame "
-//		  << blockFrame << ", endFrame " << endFrame << ", blockSize "
-//                  << m_context.blockSize << std::endl;
-
-	long completion =
-	    (((blockFrame - contextStart) / m_context.stepSize) * 99) /
-	    (contextDuration / m_context.stepSize);
-
-	// channelCount is either m_input->channelCount or 1
-
-        for (size_t ch = 0; ch < channelCount; ++ch) {
-            if (frequencyDomain) {
-                int column = (blockFrame - startFrame) / m_context.stepSize;
-                for (size_t i = 0; i <= m_context.blockSize/2; ++i) {
-                    fftModels[ch]->getValuesAt
-                        (column, i, buffers[ch][i*2], buffers[ch][i*2+1]);
-                }
-            } else {
-                getFrames(ch, channelCount, 
-                          blockFrame, m_context.blockSize, buffers[ch]);
-            }                
-        }
-
-	Vamp::Plugin::FeatureSet features = m_plugin->process
-	    (buffers, Vamp::RealTime::frame2RealTime(blockFrame, sampleRate));
-
-	for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) {
-	    Vamp::Plugin::Feature feature =
-		features[m_outputFeatureNo][fi];
-	    addFeature(blockFrame, feature);
-	}
-
-	if (blockFrame == contextStart || completion > prevCompletion) {
-	    setCompletion(completion);
-	    prevCompletion = completion;
-	}
-
-	blockFrame += m_context.stepSize;
-    }
-
-    if (m_abandoned) return;
-
-    Vamp::Plugin::FeatureSet features = m_plugin->getRemainingFeatures();
-
-    for (size_t fi = 0; fi < features[m_outputFeatureNo].size(); ++fi) {
-	Vamp::Plugin::Feature feature =
-	    features[m_outputFeatureNo][fi];
-	addFeature(blockFrame, feature);
-    }
-
-    if (frequencyDomain) {
-        for (size_t ch = 0; ch < channelCount; ++ch) {
-            delete fftModels[ch];
-        }
-    }
-
-    setCompletion(100);
-}
-
-void
-FeatureExtractionPluginTransform::getFrames(int channel, int channelCount,
-                                            long startFrame, long size,
-                                            float *buffer)
-{
-    long offset = 0;
-
-    if (startFrame < 0) {
-        for (int i = 0; i < size && startFrame + i < 0; ++i) {
-            buffer[i] = 0.0f;
-        }
-        offset = -startFrame;
-        size -= offset;
-        if (size <= 0) return;
-        startFrame = 0;
-    }
-
-    long got = getInput()->getData
-        ((channelCount == 1 ? m_context.channel : channel),
-         startFrame, size, buffer + offset);
-
-    while (got < size) {
-        buffer[offset + got] = 0.0;
-        ++got;
-    }
-
-    if (m_context.channel == -1 && channelCount == 1 &&
-        getInput()->getChannelCount() > 1) {
-        // use mean instead of sum, as plugin input
-        int cc = getInput()->getChannelCount();
-        for (long i = 0; i < size; ++i) {
-            buffer[i] /= cc;
-        }
-    }
-}
-
-void
-FeatureExtractionPluginTransform::addFeature(size_t blockFrame,
-					     const Vamp::Plugin::Feature &feature)
-{
-    size_t inputRate = m_input->getSampleRate();
-
-//    std::cerr << "FeatureExtractionPluginTransform::addFeature("
-//	      << blockFrame << ")" << std::endl;
-
-    int binCount = 1;
-    if (m_descriptor->hasFixedBinCount) {
-	binCount = m_descriptor->binCount;
-    }
-
-    size_t frame = blockFrame;
-
-    if (m_descriptor->sampleType ==
-	Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
-
-	if (!feature.hasTimestamp) {
-	    std::cerr
-		<< "WARNING: FeatureExtractionPluginTransform::addFeature: "
-		<< "Feature has variable sample rate but no timestamp!"
-		<< std::endl;
-	    return;
-	} else {
-	    frame = Vamp::RealTime::realTime2Frame(feature.timestamp, inputRate);
-	}
-
-    } else if (m_descriptor->sampleType ==
-	       Vamp::Plugin::OutputDescriptor::FixedSampleRate) {
-
-	if (feature.hasTimestamp) {
-	    //!!! warning: sampleRate may be non-integral
-	    frame = Vamp::RealTime::realTime2Frame(feature.timestamp,
-                                                   lrintf(m_descriptor->sampleRate));
-	} else {
-	    frame = m_output->getEndFrame();
-	}
-    }
-	
-    if (binCount == 0) {
-
-	SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>();
-	if (!model) return;
-	model->addPoint(SparseOneDimensionalModel::Point(frame, feature.label.c_str()));
-	
-    } else if (binCount == 1) {
-
-	float value = 0.0;
-	if (feature.values.size() > 0) value = feature.values[0];
-
-	SparseTimeValueModel *model = getOutput<SparseTimeValueModel>();
-	if (!model) return;
-	model->addPoint(SparseTimeValueModel::Point(frame, value, feature.label.c_str()));
-//        std::cerr << "SparseTimeValueModel::addPoint(" << frame << ", " << value << "), " << feature.label.c_str() << std::endl;
-
-    } else if (m_descriptor->sampleType == 
-	       Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
-
-        float pitch = 0.0;
-        if (feature.values.size() > 0) pitch = feature.values[0];
-
-        float duration = 1;
-        if (feature.values.size() > 1) duration = feature.values[1];
-        
-        float velocity = 100;
-        if (feature.values.size() > 2) velocity = feature.values[2];
-
-        NoteModel *model = getOutput<NoteModel>();
-        if (!model) return;
-
-        model->addPoint(NoteModel::Point(frame, pitch,
-                                         lrintf(duration),
-                                         feature.label.c_str()));
-	
-    } else {
-	
-	DenseThreeDimensionalModel::Column values = feature.values;
-	
-	EditableDenseThreeDimensionalModel *model =
-            getOutput<EditableDenseThreeDimensionalModel>();
-	if (!model) return;
-
-	model->setColumn(frame / model->getResolution(), values);
-    }
-}
-
-void
-FeatureExtractionPluginTransform::setCompletion(int completion)
-{
-    int binCount = 1;
-    if (m_descriptor->hasFixedBinCount) {
-	binCount = m_descriptor->binCount;
-    }
-
-//    std::cerr << "FeatureExtractionPluginTransform::setCompletion("
-//              << completion << ")" << std::endl;
-
-    if (binCount == 0) {
-
-	SparseOneDimensionalModel *model = getOutput<SparseOneDimensionalModel>();
-	if (!model) return;
-	model->setCompletion(completion);
-
-    } else if (binCount == 1) {
-
-	SparseTimeValueModel *model = getOutput<SparseTimeValueModel>();
-	if (!model) return;
-	model->setCompletion(completion);
-
-    } else if (m_descriptor->sampleType ==
-	       Vamp::Plugin::OutputDescriptor::VariableSampleRate) {
-
-	NoteModel *model = getOutput<NoteModel>();
-	if (!model) return;
-	model->setCompletion(completion);
-
-    } else {
-
-	EditableDenseThreeDimensionalModel *model =
-            getOutput<EditableDenseThreeDimensionalModel>();
-	if (!model) return;
-	model->setCompletion(completion);
-    }
-}
-
--- a/transform/FeatureExtractionPluginTransform.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,62 +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 _FEATURE_EXTRACTION_PLUGIN_TRANSFORM_H_
-#define _FEATURE_EXTRACTION_PLUGIN_TRANSFORM_H_
-
-#include "PluginTransform.h"
-
-class DenseTimeValueModel;
-
-class FeatureExtractionPluginTransform : public PluginTransform
-{
-    Q_OBJECT
-
-public:
-    FeatureExtractionPluginTransform(Model *inputModel,
-				     QString plugin,
-                                     const ExecutionContext &context,
-                                     QString configurationXml = "",
-				     QString outputName = "");
-    virtual ~FeatureExtractionPluginTransform();
-
-protected:
-    virtual void run();
-
-    Vamp::Plugin *m_plugin;
-    Vamp::Plugin::OutputDescriptor *m_descriptor;
-    int m_outputFeatureNo;
-
-    void addFeature(size_t blockFrame,
-		    const Vamp::Plugin::Feature &feature);
-
-    void setCompletion(int);
-
-    void getFrames(int channel, int channelCount,
-                   long startFrame, long size, float *buffer);
-
-    // just casts
-    DenseTimeValueModel *getInput();
-    template <typename ModelClass> ModelClass *getOutput() {
-	ModelClass *mc = dynamic_cast<ModelClass *>(m_output);
-	if (!mc) {
-	    std::cerr << "FeatureExtractionPluginTransform::getOutput: Output model not conformable" << std::endl;
-	}
-	return mc;
-    }
-};
-
-#endif
-
--- a/transform/PluginTransform.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +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 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 "PluginTransform.h"
-
-#include "vamp-sdk/PluginHostAdapter.h"
-#include "vamp-sdk/hostext/PluginWrapper.h"
-
-PluginTransform::PluginTransform(Model *inputModel,
-				 const ExecutionContext &context) :
-    Transform(inputModel),
-    m_context(context)
-{
-}
-
-PluginTransform::ExecutionContext::ExecutionContext(int _c, size_t _bs) :
-    channel(_c),
-    domain(Vamp::Plugin::TimeDomain),
-    stepSize(_bs ? _bs : 1024),
-    blockSize(_bs ? _bs : 1024),
-    windowType(HanningWindow),
-    startFrame(0),
-    duration(0),
-    sampleRate(0)
-{
-}
-
-PluginTransform::ExecutionContext::ExecutionContext(int _c, size_t _ss,
-                                                    size_t _bs, WindowType _wt) :
-    channel(_c),
-    domain(Vamp::Plugin::FrequencyDomain),
-    stepSize(_ss ? _ss : (_bs ? _bs / 2 : 512)),
-    blockSize(_bs ? _bs : 1024),
-    windowType(_wt),
-    startFrame(0),
-    duration(0),
-    sampleRate(0)
-{
-}
-
-PluginTransform::ExecutionContext::ExecutionContext(int _c,
-                                                    const Vamp::PluginBase *_plugin) :
-    channel(_c),
-    domain(Vamp::Plugin::TimeDomain),
-    stepSize(0),
-    blockSize(0),
-    windowType(HanningWindow),
-    startFrame(0),
-    duration(0),
-    sampleRate(0)
-{
-    makeConsistentWithPlugin(_plugin);
-}
-
-bool
-PluginTransform::ExecutionContext::operator==(const ExecutionContext &c)
-{
-    return (c.channel == channel &&
-            c.domain == domain &&
-            c.stepSize == stepSize &&
-            c.blockSize == blockSize &&
-            c.windowType == windowType &&
-            c.startFrame == startFrame &&
-            c.duration == duration &&
-            c.sampleRate == sampleRate);
-}
-
-void
-PluginTransform::ExecutionContext::makeConsistentWithPlugin(const Vamp::PluginBase *_plugin)
-{
-    const Vamp::Plugin *vp = dynamic_cast<const Vamp::Plugin *>(_plugin);
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::Plugin" << std::endl;
-        vp = dynamic_cast<const Vamp::PluginHostAdapter *>(_plugin); //!!! why?
-}
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::PluginHostAdapter" << std::endl;
-        vp = dynamic_cast<const Vamp::HostExt::PluginWrapper *>(_plugin); //!!! no, I mean really why?
-    }
-    if (!vp) {
-//        std::cerr << "makeConsistentWithPlugin: not a Vamp::HostExt::PluginWrapper" << std::endl;
-    }
-
-    if (!vp) {
-        domain = Vamp::Plugin::TimeDomain;
-        if (!stepSize) {
-            if (!blockSize) blockSize = 1024;
-            stepSize = blockSize;
-        } else {
-            if (!blockSize) blockSize = stepSize;
-        }
-    } else {
-        domain = vp->getInputDomain();
-        if (!stepSize) stepSize = vp->getPreferredStepSize();
-        if (!blockSize) blockSize = vp->getPreferredBlockSize();
-        if (!blockSize) blockSize = 1024;
-        if (!stepSize) {
-            if (domain == Vamp::Plugin::FrequencyDomain) {
-//                std::cerr << "frequency domain, step = " << blockSize/2 << std::endl;
-                stepSize = blockSize/2;
-            } else {
-//                std::cerr << "time domain, step = " << blockSize/2 << std::endl;
-                stepSize = blockSize;
-            }
-        }
-    }
-}
-    
-
--- a/transform/PluginTransform.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +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 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 _PLUGIN_TRANSFORM_H_
-#define _PLUGIN_TRANSFORM_H_
-
-#include "Transform.h"
-
-#include "base/Window.h"
-
-#include "vamp-sdk/Plugin.h"
-
-//!!! should this just move back up to Transform? It is after all used
-//directly in all sorts of generic places, like Document
-
-class PluginTransform : public Transform
-{
-public:
-    class ExecutionContext {
-    public:
-        // Time domain:
-        ExecutionContext(int _c = -1, size_t _bs = 0);
-        
-        // Frequency domain:
-        ExecutionContext(int _c, size_t _ss, size_t _bs, WindowType _wt);
-
-        // From plugin defaults:
-        ExecutionContext(int _c, const Vamp::PluginBase *_plugin);
-
-        bool operator==(const ExecutionContext &);
-
-        void makeConsistentWithPlugin(const Vamp::PluginBase *_plugin);
-
-        int channel;
-        Vamp::Plugin::InputDomain domain;
-        size_t stepSize;
-        size_t blockSize;
-        WindowType windowType;
-        size_t startFrame;
-        size_t duration;    // 0 -> whole thing
-        float sampleRate;   // 0 -> model's rate
-    };
-
-protected:
-    PluginTransform(Model *inputModel,
-                    const ExecutionContext &context);
-
-    ExecutionContext m_context;
-};
-
-#endif
--- a/transform/RealTimePluginTransform.cpp	Wed Oct 24 16:00:30 2007 +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 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 "RealTimePluginTransform.h"
-
-#include "plugin/RealTimePluginFactory.h"
-#include "plugin/RealTimePluginInstance.h"
-#include "plugin/PluginXml.h"
-
-#include "data/model/Model.h"
-#include "data/model/SparseTimeValueModel.h"
-#include "data/model/DenseTimeValueModel.h"
-#include "data/model/WritableWaveFileModel.h"
-#include "data/model/WaveFileModel.h"
-
-#include <iostream>
-
-RealTimePluginTransform::RealTimePluginTransform(Model *inputModel,
-                                                 QString pluginId,
-                                                 const ExecutionContext &context,
-                                                 QString configurationXml,
-                                                 QString units,
-                                                 int output) :
-    PluginTransform(inputModel, context),
-    m_pluginId(pluginId),
-    m_configurationXml(configurationXml),
-    m_units(units),
-    m_plugin(0),
-    m_outputNo(output)
-{
-    if (!m_context.blockSize) m_context.blockSize = 1024;
-
-//    std::cerr << "RealTimePluginTransform::RealTimePluginTransform: plugin " << pluginId.toStdString() << ", output " << output << std::endl;
-
-    RealTimePluginFactory *factory =
-	RealTimePluginFactory::instanceFor(pluginId);
-
-    if (!factory) {
-	std::cerr << "RealTimePluginTransform: No factory available for plugin id \""
-		  << pluginId.toStdString() << "\"" << std::endl;
-	return;
-    }
-
-    DenseTimeValueModel *input = getInput();
-    if (!input) return;
-
-    m_plugin = factory->instantiatePlugin(pluginId, 0, 0,
-                                          m_input->getSampleRate(),
-                                          m_context.blockSize,
-                                          input->getChannelCount());
-
-    if (!m_plugin) {
-	std::cerr << "RealTimePluginTransform: Failed to instantiate plugin \""
-		  << pluginId.toStdString() << "\"" << std::endl;
-	return;
-    }
-
-    if (configurationXml != "") {
-        PluginXml(m_plugin).setParametersFromXml(configurationXml);
-    }
-
-    if (m_outputNo >= 0 &&
-        m_outputNo >= int(m_plugin->getControlOutputCount())) {
-        std::cerr << "RealTimePluginTransform: Plugin has fewer than desired " << m_outputNo << " control outputs" << std::endl;
-        return;
-    }
-
-    if (m_outputNo == -1) {
-
-        size_t outputChannels = m_plugin->getAudioOutputCount();
-        if (outputChannels > input->getChannelCount()) {
-            outputChannels = input->getChannelCount();
-        }
-
-        WritableWaveFileModel *model = new WritableWaveFileModel
-            (input->getSampleRate(), outputChannels);
-
-        m_output = model;
-
-    } else {
-	
-        SparseTimeValueModel *model = new SparseTimeValueModel
-            (input->getSampleRate(), m_context.blockSize, 0.0, 0.0, false);
-
-        if (units != "") model->setScaleUnits(units);
-
-        m_output = model;
-    }
-}
-
-RealTimePluginTransform::~RealTimePluginTransform()
-{
-    delete m_plugin;
-}
-
-DenseTimeValueModel *
-RealTimePluginTransform::getInput()
-{
-    DenseTimeValueModel *dtvm =
-	dynamic_cast<DenseTimeValueModel *>(getInputModel());
-    if (!dtvm) {
-	std::cerr << "RealTimePluginTransform::getInput: WARNING: Input model is not conformable to DenseTimeValueModel" << std::endl;
-    }
-    return dtvm;
-}
-
-void
-RealTimePluginTransform::run()
-{
-    DenseTimeValueModel *input = getInput();
-    if (!input) return;
-
-    while (!input->isReady()) {
-        if (dynamic_cast<WaveFileModel *>(input)) break; // no need to wait
-        std::cerr << "RealTimePluginTransform::run: Waiting for input model to be ready..." << std::endl;
-        sleep(1);
-    }
-
-    SparseTimeValueModel *stvm = dynamic_cast<SparseTimeValueModel *>(m_output);
-    WritableWaveFileModel *wwfm = dynamic_cast<WritableWaveFileModel *>(m_output);
-    if (!stvm && !wwfm) return;
-
-    if (stvm && (m_outputNo >= int(m_plugin->getControlOutputCount()))) return;
-
-    size_t sampleRate = input->getSampleRate();
-    size_t channelCount = input->getChannelCount();
-    if (!wwfm && m_context.channel != -1) channelCount = 1;
-
-    long blockSize = m_plugin->getBufferSize();
-
-    float **inbufs = m_plugin->getAudioInputBuffers();
-
-    long startFrame = m_input->getStartFrame();
-    long   endFrame = m_input->getEndFrame();
-    
-    long contextStart = m_context.startFrame;
-    long contextDuration = m_context.duration;
-
-    if (contextStart == 0 || contextStart < startFrame) {
-        contextStart = startFrame;
-    }
-
-    if (contextDuration == 0) {
-        contextDuration = endFrame - contextStart;
-    }
-    if (contextStart + contextDuration > endFrame) {
-        contextDuration = endFrame - contextStart;
-    }
-
-    wwfm->setStartFrame(contextStart);
-
-    long blockFrame = contextStart;
-
-    long prevCompletion = 0;
-
-    long latency = m_plugin->getLatency();
-
-    while (blockFrame < contextStart + contextDuration + latency &&
-           !m_abandoned) {
-
-	long completion =
-	    (((blockFrame - contextStart) / blockSize) * 99) /
-	    ((contextDuration) / blockSize);
-
-	long got = 0;
-
-	if (channelCount == 1) {
-            if (inbufs && inbufs[0]) {
-                got = input->getData
-                    (m_context.channel, blockFrame, blockSize, inbufs[0]);
-                while (got < blockSize) {
-                    inbufs[0][got++] = 0.0;
-                }          
-            }
-            for (size_t ch = 1; ch < m_plugin->getAudioInputCount(); ++ch) {
-                for (long i = 0; i < blockSize; ++i) {
-                    inbufs[ch][i] = inbufs[0][i];
-                }
-            }
-	} else {
-	    for (size_t ch = 0; ch < channelCount; ++ch) {
-                if (inbufs && inbufs[ch]) {
-                    got = input->getData
-                        (ch, blockFrame, blockSize, inbufs[ch]);
-                    while (got < blockSize) {
-                        inbufs[ch][got++] = 0.0;
-                    }
-                }
-	    }
-            for (size_t ch = channelCount; ch < m_plugin->getAudioInputCount(); ++ch) {
-                for (long i = 0; i < blockSize; ++i) {
-                    inbufs[ch][i] = inbufs[ch % channelCount][i];
-                }
-            }
-	}
-
-/*
-        std::cerr << "Input for plugin: " << m_plugin->getAudioInputCount() << " channels "<< std::endl;
-
-        for (size_t ch = 0; ch < m_plugin->getAudioInputCount(); ++ch) {
-            std::cerr << "Input channel " << ch << std::endl;
-            for (size_t i = 0; i < 100; ++i) {
-                std::cerr << inbufs[ch][i] << " ";
-                if (isnan(inbufs[ch][i])) {
-                    std::cerr << "\n\nWARNING: NaN in audio input" << std::endl;
-                }
-            }
-        }
-*/
-
-        m_plugin->run(Vamp::RealTime::frame2RealTime(blockFrame, sampleRate));
-
-        if (stvm) {
-
-            float value = m_plugin->getControlOutputValue(m_outputNo);
-
-            long pointFrame = blockFrame;
-            if (pointFrame > latency) pointFrame -= latency;
-            else pointFrame = 0;
-
-            stvm->addPoint(SparseTimeValueModel::Point
-                           (pointFrame, value, ""));
-
-        } else if (wwfm) {
-
-            float **outbufs = m_plugin->getAudioOutputBuffers();
-
-            if (outbufs) {
-
-                if (blockFrame >= latency) {
-                    long writeSize = std::min
-                        (blockSize,
-                         contextStart + contextDuration + latency - blockFrame);
-                    wwfm->addSamples(outbufs, writeSize);
-                } else if (blockFrame + blockSize >= latency) {
-                    long offset = latency - blockFrame;
-                    long count = blockSize - offset;
-                    float **tmp = new float *[channelCount];
-                    for (size_t c = 0; c < channelCount; ++c) {
-                        tmp[c] = outbufs[c] + offset;
-                    }
-                    wwfm->addSamples(tmp, count);
-                    delete[] tmp;
-                }
-            }
-        }
-
-	if (blockFrame == contextStart || completion > prevCompletion) {
-	    if (stvm) stvm->setCompletion(completion);
-	    if (wwfm) wwfm->setCompletion(completion);
-	    prevCompletion = completion;
-	}
-        
-	blockFrame += blockSize;
-    }
-
-    if (m_abandoned) return;
-    
-    if (stvm) stvm->setCompletion(100);
-    if (wwfm) wwfm->setCompletion(100);
-}
-
--- a/transform/RealTimePluginTransform.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +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 _REAL_TIME_PLUGIN_TRANSFORM_H_
-#define _REAL_TIME_PLUGIN_TRANSFORM_H_
-
-#include "PluginTransform.h"
-#include "plugin/RealTimePluginInstance.h"
-
-class DenseTimeValueModel;
-
-class RealTimePluginTransform : public PluginTransform
-{
-public:
-    RealTimePluginTransform(Model *inputModel,
-			    QString plugin,
-                            const ExecutionContext &context,
-			    QString configurationXml = "",
-                            QString units = "",
-			    int output = -1); // -1 -> audio, 0+ -> data
-    virtual ~RealTimePluginTransform();
-
-protected:
-    virtual void run();
-
-    QString m_pluginId;
-    QString m_configurationXml;
-    QString m_units;
-
-    RealTimePluginInstance *m_plugin;
-    int m_outputNo;
-
-    // just casts
-    DenseTimeValueModel *getInput();
-};
-
-#endif
-
--- a/transform/Transform.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +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 "Transform.h"
-
-Transform::Transform(Model *m) :
-    m_input(m),
-    m_output(0),
-    m_detached(false),
-    m_abandoned(false)
-{
-}
-
-Transform::~Transform()
-{
-    m_abandoned = true;
-    wait();
-    if (!m_detached) delete m_output;
-}
-
--- a/transform/Transform.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +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 _TRANSFORM_H_
-#define _TRANSFORM_H_
-
-#include "base/Thread.h"
-
-#include "data/model/Model.h"
-
-typedef QString TransformId;
-
-/**
- * A Transform turns one data model into another.
- *
- * Typically in this application, a Transform might have a
- * DenseTimeValueModel as its input (e.g. an audio waveform) and a
- * SparseOneDimensionalModel (e.g. detected beats) as its output.
- *
- * The Transform typically runs in the background, as a separate
- * thread populating the output model.  The model is available to the
- * user of the Transform immediately, but may be initially empty until
- * the background thread has populated it.
- */
-
-class Transform : public Thread
-{
-public:
-    virtual ~Transform();
-
-    // Just a hint to the processing thread that it should give up.
-    // Caller should still wait() and/or delete the transform before
-    // assuming its input and output models are no longer required.
-    void abandon() { m_abandoned = true; }
-
-    Model *getInputModel()  { return m_input; }
-    Model *getOutputModel() { return m_output; }
-    Model *detachOutputModel() { m_detached = true; return m_output; }
-
-protected:
-    Transform(Model *m);
-
-    Model *m_input; // I don't own this
-    Model *m_output; // I own this, unless...
-    bool m_detached; // ... this is true.
-    bool m_abandoned;
-};
-
-#endif
--- a/transform/TransformFactory.cpp	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,861 +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 "TransformFactory.h"
-
-#include "FeatureExtractionPluginTransform.h"
-#include "RealTimePluginTransform.h"
-
-#include "plugin/FeatureExtractionPluginFactory.h"
-#include "plugin/RealTimePluginFactory.h"
-#include "plugin/PluginXml.h"
-
-#include "widgets/PluginParameterDialog.h"
-
-#include "data/model/DenseTimeValueModel.h"
-
-#include "vamp-sdk/PluginHostAdapter.h"
-
-#include "sv/audioio/AudioCallbackPlaySource.h" //!!! shouldn't include here
-
-#include <iostream>
-#include <set>
-
-#include <QRegExp>
-
-TransformFactory *
-TransformFactory::m_instance = new TransformFactory;
-
-TransformFactory *
-TransformFactory::getInstance()
-{
-    return m_instance;
-}
-
-TransformFactory::~TransformFactory()
-{
-}
-
-TransformFactory::TransformList
-TransformFactory::getAllTransforms()
-{
-    if (m_transforms.empty()) populateTransforms();
-
-    std::set<TransformDesc> dset;
-    for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
-	 i != m_transforms.end(); ++i) {
-	dset.insert(i->second);
-    }
-
-    TransformList list;
-    for (std::set<TransformDesc>::const_iterator i = dset.begin();
-	 i != dset.end(); ++i) {
-	list.push_back(*i);
-    }
-
-    return list;
-}
-
-std::vector<QString>
-TransformFactory::getAllTransformTypes()
-{
-    if (m_transforms.empty()) populateTransforms();
-
-    std::set<QString> types;
-    for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
-	 i != m_transforms.end(); ++i) {
-        types.insert(i->second.type);
-    }
-
-    std::vector<QString> rv;
-    for (std::set<QString>::iterator i = types.begin(); i != types.end(); ++i) {
-        rv.push_back(*i);
-    }
-
-    return rv;
-}
-
-std::vector<QString>
-TransformFactory::getTransformCategories(QString transformType)
-{
-    if (m_transforms.empty()) populateTransforms();
-
-    std::set<QString> categories;
-    for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
-         i != m_transforms.end(); ++i) {
-        if (i->second.type == transformType) {
-            categories.insert(i->second.category);
-        }
-    }
-
-    bool haveEmpty = false;
-    
-    std::vector<QString> rv;
-    for (std::set<QString>::iterator i = categories.begin(); 
-         i != categories.end(); ++i) {
-        if (*i != "") rv.push_back(*i);
-        else haveEmpty = true;
-    }
-
-    if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
-
-    return rv;
-}
-
-std::vector<QString>
-TransformFactory::getTransformMakers(QString transformType)
-{
-    if (m_transforms.empty()) populateTransforms();
-
-    std::set<QString> makers;
-    for (TransformDescriptionMap::const_iterator i = m_transforms.begin();
-         i != m_transforms.end(); ++i) {
-        if (i->second.type == transformType) {
-            makers.insert(i->second.maker);
-        }
-    }
-
-    bool haveEmpty = false;
-    
-    std::vector<QString> rv;
-    for (std::set<QString>::iterator i = makers.begin(); 
-         i != makers.end(); ++i) {
-        if (*i != "") rv.push_back(*i);
-        else haveEmpty = true;
-    }
-
-    if (haveEmpty) rv.push_back(""); // make sure empty category sorts last
-
-    return rv;
-}
-
-void
-TransformFactory::populateTransforms()
-{
-    TransformDescriptionMap transforms;
-
-    populateFeatureExtractionPlugins(transforms);
-    populateRealTimePlugins(transforms);
-
-    // disambiguate plugins with similar names
-
-    std::map<QString, int> names;
-    std::map<QString, QString> pluginSources;
-    std::map<QString, QString> pluginMakers;
-
-    for (TransformDescriptionMap::iterator i = transforms.begin();
-         i != transforms.end(); ++i) {
-
-        TransformDesc desc = i->second;
-
-        QString td = desc.name;
-        QString tn = td.section(": ", 0, 0);
-        QString pn = desc.identifier.section(":", 1, 1);
-
-        if (pluginSources.find(tn) != pluginSources.end()) {
-            if (pluginSources[tn] != pn && pluginMakers[tn] != desc.maker) {
-                ++names[tn];
-            }
-        } else {
-            ++names[tn];
-            pluginSources[tn] = pn;
-            pluginMakers[tn] = desc.maker;
-        }
-    }
-
-    std::map<QString, int> counts;
-    m_transforms.clear();
-
-    for (TransformDescriptionMap::iterator i = transforms.begin();
-         i != transforms.end(); ++i) {
-
-        TransformDesc desc = i->second;
-	QString identifier = desc.identifier;
-        QString maker = desc.maker;
-
-        QString td = desc.name;
-        QString tn = td.section(": ", 0, 0);
-        QString to = td.section(": ", 1);
-
-	if (names[tn] > 1) {
-            maker.replace(QRegExp(tr(" [\\(<].*$")), "");
-	    tn = QString("%1 [%2]").arg(tn).arg(maker);
-	}
-
-        if (to != "") {
-            desc.name = QString("%1: %2").arg(tn).arg(to);
-        } else {
-            desc.name = tn;
-        }
-
-	m_transforms[identifier] = desc;
-    }	    
-}
-
-void
-TransformFactory::populateFeatureExtractionPlugins(TransformDescriptionMap &transforms)
-{
-    std::vector<QString> plugs =
-	FeatureExtractionPluginFactory::getAllPluginIdentifiers();
-
-    for (size_t i = 0; i < plugs.size(); ++i) {
-
-	QString pluginId = plugs[i];
-
-	FeatureExtractionPluginFactory *factory =
-	    FeatureExtractionPluginFactory::instanceFor(pluginId);
-
-	if (!factory) {
-	    std::cerr << "WARNING: TransformFactory::populateTransforms: No feature extraction plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
-	    continue;
-	}
-
-	Vamp::Plugin *plugin = 
-	    factory->instantiatePlugin(pluginId, 48000);
-
-	if (!plugin) {
-	    std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to instantiate plugin " << pluginId.toLocal8Bit().data() << std::endl;
-	    continue;
-	}
-		
-	QString pluginName = plugin->getName().c_str();
-        QString category = factory->getPluginCategory(pluginId);
-
-	Vamp::Plugin::OutputList outputs =
-	    plugin->getOutputDescriptors();
-
-	for (size_t j = 0; j < outputs.size(); ++j) {
-
-	    QString transformId = QString("%1:%2")
-		    .arg(pluginId).arg(outputs[j].identifier.c_str());
-
-	    QString userName;
-            QString friendlyName;
-            QString units = outputs[j].unit.c_str();
-            QString description = plugin->getDescription().c_str();
-            QString maker = plugin->getMaker().c_str();
-            if (maker == "") maker = tr("<unknown maker>");
-
-            if (description == "") {
-                if (outputs.size() == 1) {
-                    description = tr("Extract features using \"%1\" plugin (from %2)")
-                        .arg(pluginName).arg(maker);
-                } else {
-                    description = tr("Extract features using \"%1\" output of \"%2\" plugin (from %3)")
-                        .arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
-                }
-            } else {
-                if (outputs.size() == 1) {
-                    description = tr("%1 using \"%2\" plugin (from %3)")
-                        .arg(description).arg(pluginName).arg(maker);
-                } else {
-                    description = tr("%1 using \"%2\" output of \"%3\" plugin (from %4)")
-                        .arg(description).arg(outputs[j].name.c_str()).arg(pluginName).arg(maker);
-                }
-            }                    
-
-	    if (outputs.size() == 1) {
-		userName = pluginName;
-                friendlyName = pluginName;
-	    } else {
-		userName = QString("%1: %2")
-		    .arg(pluginName)
-		    .arg(outputs[j].name.c_str());
-                friendlyName = outputs[j].name.c_str();
-	    }
-
-            bool configurable = (!plugin->getPrograms().empty() ||
-                                 !plugin->getParameterDescriptors().empty());
-
-//            std::cerr << "Feature extraction plugin transform: " << transformId.toStdString() << std::endl;
-
-	    transforms[transformId] = 
-                TransformDesc(tr("Analysis"),
-                              category,
-                              transformId,
-                              userName,
-                              friendlyName,
-                              description,
-                              maker,
-                              units,
-                              configurable);
-	}
-
-        delete plugin;
-    }
-}
-
-void
-TransformFactory::populateRealTimePlugins(TransformDescriptionMap &transforms)
-{
-    std::vector<QString> plugs =
-	RealTimePluginFactory::getAllPluginIdentifiers();
-
-    static QRegExp unitRE("[\\[\\(]([A-Za-z0-9/]+)[\\)\\]]$");
-
-    for (size_t i = 0; i < plugs.size(); ++i) {
-        
-	QString pluginId = plugs[i];
-
-        RealTimePluginFactory *factory =
-            RealTimePluginFactory::instanceFor(pluginId);
-
-	if (!factory) {
-	    std::cerr << "WARNING: TransformFactory::populateTransforms: No real time plugin factory for instance " << pluginId.toLocal8Bit().data() << std::endl;
-	    continue;
-	}
-
-        const RealTimePluginDescriptor *descriptor =
-            factory->getPluginDescriptor(pluginId);
-
-        if (!descriptor) {
-	    std::cerr << "WARNING: TransformFactory::populateTransforms: Failed to query plugin " << pluginId.toLocal8Bit().data() << std::endl;
-	    continue;
-	}
-	
-//!!!        if (descriptor->controlOutputPortCount == 0 ||
-//            descriptor->audioInputPortCount == 0) continue;
-
-//        std::cout << "TransformFactory::populateRealTimePlugins: plugin " << pluginId.toStdString() << " has " << descriptor->controlOutputPortCount << " control output ports, " << descriptor->audioOutputPortCount << " audio outputs, " << descriptor->audioInputPortCount << " audio inputs" << std::endl;
-	
-	QString pluginName = descriptor->name.c_str();
-        QString category = factory->getPluginCategory(pluginId);
-        bool configurable = (descriptor->parameterCount > 0);
-        QString maker = descriptor->maker.c_str();
-        if (maker == "") maker = tr("<unknown maker>");
-
-        if (descriptor->audioInputPortCount > 0) {
-
-            for (size_t j = 0; j < descriptor->controlOutputPortCount; ++j) {
-
-                QString transformId = QString("%1:%2").arg(pluginId).arg(j);
-                QString userName;
-                QString units;
-                QString portName;
-
-                if (j < descriptor->controlOutputPortNames.size() &&
-                    descriptor->controlOutputPortNames[j] != "") {
-
-                    portName = descriptor->controlOutputPortNames[j].c_str();
-
-                    userName = tr("%1: %2")
-                        .arg(pluginName)
-                        .arg(portName);
-
-                    if (unitRE.indexIn(portName) >= 0) {
-                        units = unitRE.cap(1);
-                    }
-
-                } else if (descriptor->controlOutputPortCount > 1) {
-
-                    userName = tr("%1: Output %2")
-                        .arg(pluginName)
-                        .arg(j + 1);
-
-                } else {
-
-                    userName = pluginName;
-                }
-
-                QString description;
-
-                if (portName != "") {
-                    description = tr("Extract \"%1\" data output from \"%2\" effect plugin (from %3)")
-                        .arg(portName)
-                        .arg(pluginName)
-                        .arg(maker);
-                } else {
-                    description = tr("Extract data output %1 from \"%2\" effect plugin (from %3)")
-                        .arg(j + 1)
-                        .arg(pluginName)
-                        .arg(maker);
-                }
-
-                transforms[transformId] = 
-                    TransformDesc(tr("Effects Data"),
-                                  category,
-                                  transformId,
-                                  userName,
-                                  userName,
-                                  description,
-                                  maker,
-                                  units,
-                                  configurable);
-            }
-        }
-
-        if (!descriptor->isSynth || descriptor->audioInputPortCount > 0) {
-
-            if (descriptor->audioOutputPortCount > 0) {
-
-                QString transformId = QString("%1:A").arg(pluginId);
-                QString type = tr("Effects");
-
-                QString description = tr("Transform audio signal with \"%1\" effect plugin (from %2)")
-                    .arg(pluginName)
-                    .arg(maker);
-
-                if (descriptor->audioInputPortCount == 0) {
-                    type = tr("Generators");
-                    QString description = tr("Generate audio signal using \"%1\" plugin (from %2)")
-                        .arg(pluginName)
-                        .arg(maker);
-                }
-
-                transforms[transformId] =
-                    TransformDesc(type,
-                                  category,
-                                  transformId,
-                                  pluginName,
-                                  pluginName,
-                                  description,
-                                  maker,
-                                  "",
-                                  configurable);
-            }
-        }
-    }
-}
-
-QString
-TransformFactory::getTransformName(TransformId identifier)
-{
-    if (m_transforms.find(identifier) != m_transforms.end()) {
-	return m_transforms[identifier].name;
-    } else return "";
-}
-
-QString
-TransformFactory::getTransformFriendlyName(TransformId identifier)
-{
-    if (m_transforms.find(identifier) != m_transforms.end()) {
-	return m_transforms[identifier].friendlyName;
-    } else return "";
-}
-
-QString
-TransformFactory::getTransformUnits(TransformId identifier)
-{
-    if (m_transforms.find(identifier) != m_transforms.end()) {
-	return m_transforms[identifier].units;
-    } else return "";
-}
-
-bool
-TransformFactory::isTransformConfigurable(TransformId identifier)
-{
-    if (m_transforms.find(identifier) != m_transforms.end()) {
-	return m_transforms[identifier].configurable;
-    } else return false;
-}
-
-bool
-TransformFactory::getTransformChannelRange(TransformId identifier,
-                                           int &min, int &max)
-{
-    QString id = identifier.section(':', 0, 2);
-
-    if (FeatureExtractionPluginFactory::instanceFor(id)) {
-
-        Vamp::Plugin *plugin = 
-            FeatureExtractionPluginFactory::instanceFor(id)->
-            instantiatePlugin(id, 48000);
-        if (!plugin) return false;
-
-        min = plugin->getMinChannelCount();
-        max = plugin->getMaxChannelCount();
-        delete plugin;
-
-        return true;
-
-    } else if (RealTimePluginFactory::instanceFor(id)) {
-
-        const RealTimePluginDescriptor *descriptor = 
-            RealTimePluginFactory::instanceFor(id)->
-            getPluginDescriptor(id);
-        if (!descriptor) return false;
-
-        min = descriptor->audioInputPortCount;
-        max = descriptor->audioInputPortCount;
-
-        return true;
-    }
-
-    return false;
-}
-
-bool
-TransformFactory::getChannelRange(TransformId identifier, Vamp::PluginBase *plugin,
-                                  int &minChannels, int &maxChannels)
-{
-    Vamp::Plugin *vp = 0;
-    if ((vp = dynamic_cast<Vamp::Plugin *>(plugin)) ||
-        (vp = dynamic_cast<Vamp::PluginHostAdapter *>(plugin))) {
-        minChannels = vp->getMinChannelCount();
-        maxChannels = vp->getMaxChannelCount();
-        return true;
-    } else {
-        return getTransformChannelRange(identifier, minChannels, maxChannels);
-    }
-}
-
-Model *
-TransformFactory::getConfigurationForTransform(TransformId identifier,
-                                               const std::vector<Model *> &candidateInputModels,
-                                               PluginTransform::ExecutionContext &context,
-                                               QString &configurationXml,
-                                               AudioCallbackPlaySource *source,
-                                               size_t startFrame,
-                                               size_t duration)
-{
-    if (candidateInputModels.empty()) return 0;
-
-    //!!! This will need revision -- we'll have to have a callback
-    //from the dialog for when the candidate input model is changed,
-    //as we'll need to reinitialise the channel settings in the dialog
-    Model *inputModel = candidateInputModels[0]; //!!! for now
-    QStringList candidateModelNames;
-    std::map<QString, Model *> modelMap;
-    for (size_t i = 0; i < candidateInputModels.size(); ++i) {
-        QString modelName = candidateInputModels[i]->objectName();
-        QString origModelName = modelName;
-        int dupcount = 1;
-        while (modelMap.find(modelName) != modelMap.end()) {
-            modelName = tr("%1 <%2>").arg(origModelName).arg(++dupcount);
-        }
-        modelMap[modelName] = candidateInputModels[i];
-        candidateModelNames.push_back(modelName);
-    }
-
-    QString id = identifier.section(':', 0, 2);
-    QString output = identifier.section(':', 3);
-    QString outputLabel = "";
-    QString outputDescription = "";
-    
-    bool ok = false;
-    configurationXml = m_lastConfigurations[identifier];
-
-//    std::cerr << "last configuration: " << configurationXml.toStdString() << std::endl;
-
-    Vamp::PluginBase *plugin = 0;
-
-    bool frequency = false;
-    bool effect = false;
-    bool generator = false;
-
-    if (FeatureExtractionPluginFactory::instanceFor(id)) {
-
-        std::cerr << "getConfigurationForTransform: instantiating Vamp plugin" << std::endl;
-
-        Vamp::Plugin *vp =
-            FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
-            (id, inputModel->getSampleRate());
-
-        if (vp) {
-
-            plugin = vp;
-            frequency = (vp->getInputDomain() == Vamp::Plugin::FrequencyDomain);
-
-            std::vector<Vamp::Plugin::OutputDescriptor> od =
-                vp->getOutputDescriptors();
-            if (od.size() > 1) {
-                for (size_t i = 0; i < od.size(); ++i) {
-                    if (od[i].identifier == output.toStdString()) {
-                        outputLabel = od[i].name.c_str();
-                        outputDescription = od[i].description.c_str();
-                        break;
-                    }
-                }
-            }
-        }
-
-    } else if (RealTimePluginFactory::instanceFor(id)) {
-
-        RealTimePluginFactory *factory = RealTimePluginFactory::instanceFor(id);
-        const RealTimePluginDescriptor *desc = factory->getPluginDescriptor(id);
-
-        if (desc->audioInputPortCount > 0 && 
-            desc->audioOutputPortCount > 0 &&
-            !desc->isSynth) {
-            effect = true;
-        }
-
-        if (desc->audioInputPortCount == 0) {
-            generator = true;
-        }
-
-        if (output != "A") {
-            int outputNo = output.toInt();
-            if (outputNo >= 0 && outputNo < int(desc->controlOutputPortCount)) {
-                outputLabel = desc->controlOutputPortNames[outputNo].c_str();
-            }
-        }
-
-        size_t sampleRate = inputModel->getSampleRate();
-        size_t blockSize = 1024;
-        size_t channels = 1;
-        if (effect && source) {
-            sampleRate = source->getTargetSampleRate();
-            blockSize = source->getTargetBlockSize();
-            channels = source->getTargetChannelCount();
-        }
-
-        RealTimePluginInstance *rtp = factory->instantiatePlugin
-            (id, 0, 0, sampleRate, blockSize, channels);
-
-        plugin = rtp;
-
-        if (effect && source && rtp) {
-            source->setAuditioningPlugin(rtp);
-        }
-    }
-
-    if (plugin) {
-
-        context = PluginTransform::ExecutionContext(context.channel, plugin);
-
-        if (configurationXml != "") {
-            PluginXml(plugin).setParametersFromXml(configurationXml);
-        }
-
-        int sourceChannels = 1;
-        if (dynamic_cast<DenseTimeValueModel *>(inputModel)) {
-            sourceChannels = dynamic_cast<DenseTimeValueModel *>(inputModel)
-                ->getChannelCount();
-        }
-
-        int minChannels = 1, maxChannels = sourceChannels;
-        getChannelRange(identifier, plugin, minChannels, maxChannels);
-
-        int targetChannels = sourceChannels;
-        if (!effect) {
-            if (sourceChannels < minChannels) targetChannels = minChannels;
-            if (sourceChannels > maxChannels) targetChannels = maxChannels;
-        }
-
-        int defaultChannel = context.channel;
-
-        PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
-
-        if (candidateModelNames.size() > 1 && !generator) {
-            dialog->setCandidateInputModels(candidateModelNames);
-        }
-
-        if (startFrame != 0 || duration != 0) {
-            dialog->setShowSelectionOnlyOption(true);
-        }
-
-        if (targetChannels > 0) {
-            dialog->setChannelArrangement(sourceChannels, targetChannels,
-                                          defaultChannel);
-        }
-        
-        dialog->setOutputLabel(outputLabel, outputDescription);
-        
-        dialog->setShowProcessingOptions(true, frequency);
-
-        if (dialog->exec() == QDialog::Accepted) {
-            ok = true;
-        }
-
-        QString selectedInput = dialog->getInputModel();
-        if (selectedInput != "") {
-            if (modelMap.find(selectedInput) != modelMap.end()) {
-                inputModel = modelMap[selectedInput];
-                std::cerr << "Found selected input \"" << selectedInput.toStdString() << "\" in model map, result is " << inputModel << std::endl;
-            } else {
-                std::cerr << "Failed to find selected input \"" << selectedInput.toStdString() << "\" in model map" << std::endl;
-            }
-        } else {
-            std::cerr << "Selected input empty: \"" << selectedInput.toStdString() << "\"" << std::endl;
-        }
-
-        configurationXml = PluginXml(plugin).toXmlString();
-        context.channel = dialog->getChannel();
-        
-        if (startFrame != 0 || duration != 0) {
-            if (dialog->getSelectionOnly()) {
-                context.startFrame = startFrame;
-                context.duration = duration;
-            }
-        }
-
-        dialog->getProcessingParameters(context.stepSize,
-                                        context.blockSize,
-                                        context.windowType);
-
-        context.makeConsistentWithPlugin(plugin);
-
-        delete dialog;
-
-        if (effect && source) {
-            source->setAuditioningPlugin(0); // will delete our plugin
-        } else {
-            delete plugin;
-        }
-    }
-
-    if (ok) m_lastConfigurations[identifier] = configurationXml;
-
-    return ok ? inputModel : 0;
-}
-
-PluginTransform::ExecutionContext
-TransformFactory::getDefaultContextForTransform(TransformId identifier,
-                                                Model *inputModel)
-{
-    PluginTransform::ExecutionContext context(-1);
-
-    QString id = identifier.section(':', 0, 2);
-
-    if (FeatureExtractionPluginFactory::instanceFor(id)) {
-
-        Vamp::Plugin *vp =
-            FeatureExtractionPluginFactory::instanceFor(id)->instantiatePlugin
-            (id, inputModel ? inputModel->getSampleRate() : 48000);
-
-        if (vp) {
-            context = PluginTransform::ExecutionContext(-1, vp);
-            delete vp;
-        }
-    }
-
-    return context;
-}
-
-Transform *
-TransformFactory::createTransform(TransformId identifier, Model *inputModel,
-                                  const PluginTransform::ExecutionContext &context,
-                                  QString configurationXml)
-{
-    Transform *transform = 0;
-
-    QString id = identifier.section(':', 0, 2);
-    QString output = identifier.section(':', 3);
-
-    if (FeatureExtractionPluginFactory::instanceFor(id)) {
-        transform = new FeatureExtractionPluginTransform(inputModel,
-                                                         id,
-                                                         context,
-                                                         configurationXml,
-                                                         output);
-    } else if (RealTimePluginFactory::instanceFor(id)) {
-        transform = new RealTimePluginTransform(inputModel,
-                                                id,
-                                                context,
-                                                configurationXml,
-                                                getTransformUnits(identifier),
-                                                output == "A" ? -1 :
-                                                output.toInt());
-    } else {
-        std::cerr << "TransformFactory::createTransform: Unknown transform \""
-                  << identifier.toStdString() << "\"" << std::endl;
-        return transform;
-    }
-
-    if (transform) transform->setObjectName(identifier);
-    return transform;
-}
-
-Model *
-TransformFactory::transform(TransformId identifier, Model *inputModel,
-                            const PluginTransform::ExecutionContext &context,
-                            QString configurationXml)
-{
-    Transform *t = createTransform(identifier, inputModel, context,
-                                   configurationXml);
-
-    if (!t) return 0;
-
-    connect(t, SIGNAL(finished()), this, SLOT(transformFinished()));
-
-    m_runningTransforms.insert(t);
-
-    t->start();
-    Model *model = t->detachOutputModel();
-
-    if (model) {
-        QString imn = inputModel->objectName();
-        QString trn = getTransformFriendlyName(identifier);
-        if (imn != "") {
-            if (trn != "") {
-                model->setObjectName(tr("%1: %2").arg(imn).arg(trn));
-            } else {
-                model->setObjectName(imn);
-            }
-        } else if (trn != "") {
-            model->setObjectName(trn);
-        }
-    } else {
-        t->wait();
-    }
-
-    return model;
-}
-
-void
-TransformFactory::transformFinished()
-{
-    QObject *s = sender();
-    Transform *transform = dynamic_cast<Transform *>(s);
-    
-    std::cerr << "TransformFactory::transformFinished(" << transform << ")" << std::endl;
-
-    if (!transform) {
-	std::cerr << "WARNING: TransformFactory::transformFinished: sender is not a transform" << std::endl;
-	return;
-    }
-
-    if (m_runningTransforms.find(transform) == m_runningTransforms.end()) {
-        std::cerr << "WARNING: TransformFactory::transformFinished(" 
-                  << transform
-                  << "): I have no record of this transform running!"
-                  << std::endl;
-    }
-
-    m_runningTransforms.erase(transform);
-
-    transform->wait(); // unnecessary but reassuring
-    delete transform;
-}
-
-void
-TransformFactory::modelAboutToBeDeleted(Model *m)
-{
-    TransformSet affected;
-
-    for (TransformSet::iterator i = m_runningTransforms.begin();
-         i != m_runningTransforms.end(); ++i) {
-
-        Transform *t = *i;
-
-        if (t->getInputModel() == m || t->getOutputModel() == m) {
-            affected.insert(t);
-        }
-    }
-
-    for (TransformSet::iterator i = affected.begin();
-         i != affected.end(); ++i) {
-
-        Transform *t = *i;
-
-        t->abandon();
-
-        t->wait(); // this should eventually call back on
-                   // transformFinished, which will remove from
-                   // m_runningTransforms and delete.
-    }
-}
-
--- a/transform/TransformFactory.h	Wed Oct 24 16:00:30 2007 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,185 +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 _TRANSFORM_FACTORY_H_
-#define _TRANSFORM_FACTORY_H_
-
-#include "Transform.h"
-#include "PluginTransform.h"
-
-#include <map>
-#include <set>
-
-namespace Vamp { class PluginBase; }
-
-class AudioCallbackPlaySource;
-
-class TransformFactory : public QObject
-{
-    Q_OBJECT
-
-public:
-    virtual ~TransformFactory();
-
-    static TransformFactory *getInstance();
-
-    // The identifier is intended to be computer-referenceable, and
-    // unique within the application.  The name is intended to be
-    // human readable.  In principle it doesn't have to be unique, but
-    // the factory will add suffixes to ensure that it is, all the
-    // same (just to avoid user confusion).  The friendly name is a
-    // shorter version of the name.  The type is also intended to be
-    // user-readable, for use in menus.
-
-    struct TransformDesc {
-
-        TransformDesc() { }
-	TransformDesc(QString _type, QString _category,
-                      TransformId _identifier, QString _name,
-                      QString _friendlyName, QString _description,
-                      QString _maker, QString _units, bool _configurable) :
-	    type(_type), category(_category),
-            identifier(_identifier), name(_name),
-            friendlyName(_friendlyName), description(_description),
-            maker(_maker), units(_units), configurable(_configurable) { }
-
-        QString type; // e.g. feature extraction plugin
-        QString category; // e.g. time > onsets
-	TransformId identifier; // e.g. vamp:vamp-aubio:aubioonset
-	QString name; // plugin's name if 1 output, else "name: output"
-        QString friendlyName; // short text for layer name
-        QString description; // sentence describing transform
-        QString maker;
-        QString units;
-        bool configurable;
-
-        bool operator<(const TransformDesc &od) const {
-            return (name < od.name);
-        };
-    };
-    typedef std::vector<TransformDesc> TransformList;
-
-    TransformList getAllTransforms();
-
-    std::vector<QString> getAllTransformTypes();
-
-    std::vector<QString> getTransformCategories(QString transformType);
-    std::vector<QString> getTransformMakers(QString transformType);
-
-    /**
-     * Get a configuration XML string for the given transform (by
-     * asking the user, most likely).  Returns the selected input
-     * model if the transform is acceptable, 0 if the operation should
-     * be cancelled.  Audio callback play source may be used to
-     * audition effects plugins, if provided.
-     */
-    Model *getConfigurationForTransform(TransformId identifier,
-                                        const std::vector<Model *> &candidateInputModels,
-                                        PluginTransform::ExecutionContext &context,
-                                        QString &configurationXml,
-                                        AudioCallbackPlaySource *source = 0,
-                                        size_t startFrame = 0,
-                                        size_t duration = 0);
-
-    /**
-     * Get the default execution context for the given transform
-     * and input model (if known).
-     */
-    PluginTransform::ExecutionContext getDefaultContextForTransform(TransformId identifier,
-                                                                    Model *inputModel = 0);
-
-    /**
-     * Return the output model resulting from applying the named
-     * transform to the given input model.  The transform may still be
-     * working in the background when the model is returned; check the
-     * output model's isReady completion status for more details.
-     *
-     * If the transform is unknown or the input model is not an
-     * appropriate type for the given transform, or if some other
-     * problem occurs, return 0.
-     * 
-     * The returned model is owned by the caller and must be deleted
-     * when no longer needed.
-     */
-    Model *transform(TransformId identifier, Model *inputModel,
-                     const PluginTransform::ExecutionContext &context,
-                     QString configurationXml = "");
-
-    /**
-     * Full name of a transform, suitable for putting on a menu.
-     */
-    QString getTransformName(TransformId identifier);
-
-    /**
-     * Brief but friendly name of a transform, suitable for use
-     * as the name of the output layer.
-     */
-    QString getTransformFriendlyName(TransformId identifier);
-
-    QString getTransformUnits(TransformId identifier);
-
-    /**
-     * Return true if the transform has any configurable parameters,
-     * i.e. if getConfigurationForTransform can ever return a non-trivial
-     * (not equivalent to empty) configuration string.
-     */
-    bool isTransformConfigurable(TransformId identifier);
-
-    /**
-     * If the transform has a prescribed number or range of channel
-     * inputs, return true and set minChannels and maxChannels to the
-     * minimum and maximum number of channel inputs the transform can
-     * accept.  Return false if it doesn't care.
-     */
-    bool getTransformChannelRange(TransformId identifier,
-                                  int &minChannels, int &maxChannels);
-	
-protected slots:
-    void transformFinished();
-
-    void modelAboutToBeDeleted(Model *);
-
-protected:
-    Transform *createTransform(TransformId identifier, Model *inputModel,
-                               const PluginTransform::ExecutionContext &context,
-                               QString configurationXml);
-
-    struct TransformIdent
-    {
-        TransformId identifier;
-        QString configurationXml;
-    };
-
-    typedef std::map<TransformId, QString> TransformConfigurationMap;
-    TransformConfigurationMap m_lastConfigurations;
-
-    typedef std::map<TransformId, TransformDesc> TransformDescriptionMap;
-    TransformDescriptionMap m_transforms;
-
-    typedef std::set<Transform *> TransformSet;
-    TransformSet m_runningTransforms;
-
-    void populateTransforms();
-    void populateFeatureExtractionPlugins(TransformDescriptionMap &);
-    void populateRealTimePlugins(TransformDescriptionMap &);
-
-    bool getChannelRange(TransformId identifier,
-                         Vamp::PluginBase *plugin, int &min, int &max);
-
-    static TransformFactory *m_instance;
-};
-
-
-#endif