changeset 519:a2a8fa0eed08 3.0-plus-imaf

Merge branches 3.0-integration and imaf_enc to 3.0-plus-imaf
author Chris Cannam
date Wed, 20 Apr 2016 12:06:28 +0100
parents f7ec9e410108 (diff) 428ce32a8dd9 (current diff)
children
files framework/MainWindowBase.cpp
diffstat 45 files changed, 5984 insertions(+), 6082 deletions(-) [+]
line wrap: on
line diff
--- a/acinclude.m4	Tue Jul 14 15:04:45 2015 +0100
+++ b/acinclude.m4	Wed Apr 20 12:06:28 2016 +0100
@@ -69,6 +69,9 @@
    	AC_CHECK_PROG(QMAKE, qmake-qt5, $QTDIR/bin/qmake-qt5,,$QTDIR/bin/)
 fi
 if test x$QMAKE = x ; then
+   	AC_CHECK_PROG(QMAKE, qt5-qmake, $QTDIR/bin/qt5-qmake,,$QTDIR/bin/)
+fi
+if test x$QMAKE = x ; then
    	AC_CHECK_PROG(QMAKE, qmake, $QTDIR/bin/qmake,,$QTDIR/bin/)
 fi
 if test x$QMAKE = x ; then
@@ -78,6 +81,9 @@
    	AC_CHECK_PROG(QMAKE, qmake-qt5, qmake-qt5,,$PATH)
 fi
 if test x$QMAKE = x ; then
+   	AC_CHECK_PROG(QMAKE, qt5-qmake, qt5-qmake,,$PATH)
+fi
+if test x$QMAKE = x ; then
    	AC_CHECK_PROG(QMAKE, qmake, qmake,,$PATH)
 fi
 if test x$QMAKE = x ; then
@@ -112,3 +118,146 @@
 
 ])
 
+# From autoconf archive:
+
+# ============================================================================
+#  http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx_11.html
+# ============================================================================
+#
+# SYNOPSIS
+#
+#   AX_CXX_COMPILE_STDCXX_11([ext|noext],[mandatory|optional])
+#
+# DESCRIPTION
+#
+#   Check for baseline language coverage in the compiler for the C++11
+#   standard; if necessary, add switches to CXXFLAGS to enable support.
+#
+#   The first argument, if specified, indicates whether you insist on an
+#   extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+#   -std=c++11).  If neither is specified, you get whatever works, with
+#   preference for an extended mode.
+#
+#   The second argument, if specified 'mandatory' or if left unspecified,
+#   indicates that baseline C++11 support is required and that the macro
+#   should error out if no mode with that support is found.  If specified
+#   'optional', then configuration proceeds regardless, after defining
+#   HAVE_CXX11 if and only if a supporting mode is found.
+#
+# LICENSE
+#
+#   Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+#   Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+#   Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+#   Copyright (c) 2014 Alexey Sokolov <sokolov@google.com>
+#
+#   Copying and distribution of this file, with or without modification, are
+#   permitted in any medium without royalty provided the copyright notice
+#   and this notice are preserved. This file is offered as-is, without any
+#   warranty.
+
+m4_define([_AX_CXX_COMPILE_STDCXX_11_testbody], [[
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    struct Base {
+    virtual void f() {}
+    };
+    struct Child : public Base {
+    virtual void f() override {}
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);
+
+    auto d = a;
+    auto l = [](){};
+]])
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX_11], [dnl
+  m4_if([$1], [], [],
+        [$1], [ext], [],
+        [$1], [noext], [],
+        [m4_fatal([invalid argument `$1' to AX_CXX_COMPILE_STDCXX_11])])dnl
+  m4_if([$2], [], [ax_cxx_compile_cxx11_required=true],
+        [$2], [mandatory], [ax_cxx_compile_cxx11_required=true],
+        [$2], [optional], [ax_cxx_compile_cxx11_required=false],
+        [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX_11])])
+  AC_LANG_PUSH([C++])dnl
+  ac_success=no
+  AC_CACHE_CHECK(whether $CXX supports C++11 features by default,
+  ax_cv_cxx_compile_cxx11,
+  [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+    [ax_cv_cxx_compile_cxx11=yes],
+    [ax_cv_cxx_compile_cxx11=no])])
+  if test x$ax_cv_cxx_compile_cxx11 = xyes; then
+    ac_success=yes
+  fi
+
+  m4_if([$1], [noext], [], [dnl
+  if test x$ac_success = xno; then
+    for switch in -std=gnu++11 -std=gnu++0x; do
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
+                     $cachevar,
+        [ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXXFLAGS="$ac_save_CXXFLAGS"])
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+
+  m4_if([$1], [ext], [], [dnl
+  if test x$ac_success = xno; then
+    for switch in -std=c++11 -std=c++0x; do
+      cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx11_$switch])
+      AC_CACHE_CHECK(whether $CXX supports C++11 features with $switch,
+                     $cachevar,
+        [ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_11_testbody])],
+          [eval $cachevar=yes],
+          [eval $cachevar=no])
+         CXXFLAGS="$ac_save_CXXFLAGS"])
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi])
+  AC_LANG_POP([C++])
+  if test x$ax_cxx_compile_cxx11_required = xtrue; then
+    if test x$ac_success = xno; then
+      AC_MSG_ERROR([*** A compiler with support for C++11 language features is required.])
+    fi
+  else
+    if test x$ac_success = xno; then
+      HAVE_CXX11=0
+      AC_MSG_NOTICE([No compiler with C++11 support was found])
+    else
+      HAVE_CXX11=1
+      AC_DEFINE(HAVE_CXX11,1,
+                [define if the compiler supports basic C++11 syntax])
+    fi
+
+    AC_SUBST(HAVE_CXX11)
+  fi
+])
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/AudioCallbackPlaySource.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,1883 @@
+/* -*- 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 "base/ViewManagerBase.h"
+#include "base/PlayParameterRepository.h"
+#include "base/Preferences.h"
+#include "data/model/DenseTimeValueModel.h"
+#include "data/model/WaveFileModel.h"
+#include "data/model/ReadOnlyWaveFileModel.h"
+#include "data/model/SparseOneDimensionalModel.h"
+#include "plugin/RealTimePluginInstance.h"
+
+#include "bqaudioio/SystemPlaybackTarget.h"
+
+#include <rubberband/RubberBandStretcher.h>
+using namespace RubberBand;
+
+#include <iostream>
+#include <cassert>
+
+//#define DEBUG_AUDIO_PLAY_SOURCE 1
+//#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1
+
+static const int DEFAULT_RING_BUFFER_SIZE = 131071;
+
+AudioCallbackPlaySource::AudioCallbackPlaySource(ViewManagerBase *manager,
+                                                 QString clientName) :
+    m_viewManager(manager),
+    m_audioGenerator(new AudioGenerator()),
+    m_clientName(clientName.toUtf8().data()),
+    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_target(0),
+    m_lastRetrievalTimestamp(0.0),
+    m_lastRetrievedBlockSize(0),
+    m_trustworthyTimestamps(true),
+    m_lastCurrentFrame(0),
+    m_playing(false),
+    m_exiting(false),
+    m_lastModelEndFrame(0),
+    m_ringBufferSize(DEFAULT_RING_BUFFER_SIZE),
+    m_outputLeft(0.0),
+    m_outputRight(0.0),
+    m_auditioningPlugin(0),
+    m_auditioningPluginBypassed(false),
+    m_playStartFrame(0),
+    m_playStartFramePassed(false),
+    m_timeStretcher(0),
+    m_monoStretcher(0),
+    m_stretchRatio(1.0),
+    m_stretchMono(false),
+    m_stretcherInputCount(0),
+    m_stretcherInputs(0),
+    m_stretcherInputSizes(0),
+    m_fillThread(0),
+    m_converter(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(this, SIGNAL(playStatusChanged(bool)),
+            m_viewManager, SLOT(playStatusChanged(bool)));
+
+    connect(PlayParameterRepository::getInstance(),
+	    SIGNAL(playParametersChanged(PlayParameters *)),
+	    this, SLOT(playParametersChanged(PlayParameters *)));
+
+    connect(Preferences::getInstance(),
+            SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
+            this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
+}
+
+AudioCallbackPlaySource::~AudioCallbackPlaySource()
+{
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    SVDEBUG << "AudioCallbackPlaySource::~AudioCallbackPlaySource entering" << endl;
+#endif
+    m_exiting = true;
+
+    if (m_fillThread) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource dtor: awakening thread" << endl;
+#endif
+        m_condition.wakeAll();
+	m_fillThread->wait();
+	delete m_fillThread;
+    }
+
+    clearModels();
+    
+    if (m_readBuffers != m_writeBuffers) {
+	delete m_readBuffers;
+    }
+
+    delete m_writeBuffers;
+
+    delete m_audioGenerator;
+
+    for (int i = 0; i < m_stretcherInputCount; ++i) {
+        delete[] m_stretcherInputs[i];
+    }
+    delete[] m_stretcherInputSizes;
+    delete[] m_stretcherInputs;
+
+    delete m_timeStretcher;
+    delete m_monoStretcher;
+
+    m_bufferScavenger.scavenge(true);
+    m_pluginScavenger.scavenge(true);
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    SVDEBUG << "AudioCallbackPlaySource::~AudioCallbackPlaySource finishing" << endl;
+#endif
+}
+
+void
+AudioCallbackPlaySource::addModel(Model *model)
+{
+    if (m_models.find(model) != m_models.end()) return;
+
+    bool willPlay = 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;
+
+    int modelChannels = 1;
+    ReadOnlyWaveFileModel *rowfm = qobject_cast<ReadOnlyWaveFileModel *>(model);
+    if (rowfm) modelChannels = rowfm->getChannelCount();
+    if (modelChannels > m_sourceChannelCount) {
+	m_sourceChannelCount = modelChannels;
+    }
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource: Adding model with " << modelChannels << " channels at rate " << model->getSampleRate() << endl;
+#endif
+
+    if (m_sourceSampleRate == 0) {
+
+	m_sourceSampleRate = model->getSampleRate();
+	srChanged = true;
+
+    } else if (model->getSampleRate() != m_sourceSampleRate) {
+
+        // If this is a read-only wave file model and we have no
+        // other, we can just switch to this model's sample rate
+
+        if (rowfm) {
+
+            bool conflicting = false;
+
+            for (std::set<Model *>::const_iterator i = m_models.begin();
+                 i != m_models.end(); ++i) {
+                // Only read-only wave file models should 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
+                ReadOnlyWaveFileModel *other =
+                    qobject_cast<ReadOnlyWaveFileModel *>(*i);
+                if (other && other != rowfm &&
+                    other->getSampleRate() != model->getSampleRate() &&
+                    other->getSampleRate() == m_sourceSampleRate) {
+                    SVDEBUG << "AudioCallbackPlaySource::addModel: Conflicting wave file model " << *i << " found" << endl;
+                    conflicting = true;
+                    break;
+                }
+            }
+
+            if (conflicting) {
+
+                SVDEBUG << "AudioCallbackPlaySource::addModel: ERROR: "
+                          << "New model sample rate does not match" << endl
+                          << "existing model(s) (new " << model->getSampleRate()
+                          << " vs " << m_sourceSampleRate
+                          << "), playback will be wrong"
+                          << endl;
+                
+                emit sampleRateMismatch(model->getSampleRate(),
+                                        m_sourceSampleRate,
+                                        false);
+            } else {
+                m_sourceSampleRate = model->getSampleRate();
+                srChanged = true;
+            }
+        }
+    }
+
+    if (!m_writeBuffers || (int)m_writeBuffers->size() < getTargetChannelCount()) {
+	clearRingBuffers(true, getTargetChannelCount());
+	buffersChanged = true;
+    } else {
+	if (willPlay) clearRingBuffers(true);
+    }
+
+    if (buffersChanged || srChanged) {
+	if (m_converter) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+            cerr << "AudioCallbackPlaySource::addModel: Buffers or sample rate changed, deleting existing SR converter" << endl;
+#endif
+	    src_delete(m_converter);
+	    m_converter = 0;
+	}
+    }
+
+    rebuildRangeLists();
+
+    m_mutex.unlock();
+
+    initialiseConverter();
+    
+    m_audioGenerator->setTargetChannelCount(getTargetChannelCount());
+
+    if (!m_fillThread) {
+	m_fillThread = new FillThread(*this);
+	m_fillThread->start();
+    }
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s) -- emitting modelReplaced" << endl;
+#endif
+
+    if (buffersChanged || srChanged) {
+	emit modelReplaced();
+    }
+
+    connect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
+            this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t)));
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource::addModel: awakening thread" << endl;
+#endif
+
+    m_condition.wakeAll();
+}
+
+void
+AudioCallbackPlaySource::modelChangedWithin(sv_frame_t 
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+                                            startFrame
+#endif
+                                            , sv_frame_t endFrame)
+{
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    SVDEBUG << "AudioCallbackPlaySource::modelChangedWithin(" << startFrame << "," << endFrame << ")" << endl;
+#endif
+    if (endFrame > m_lastModelEndFrame) {
+        m_lastModelEndFrame = endFrame;
+        rebuildRangeLists();
+    }
+}
+
+void
+AudioCallbackPlaySource::removeModel(Model *model)
+{
+    m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource::removeModel(" << model << ")" << endl;
+#endif
+
+    disconnect(model, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
+               this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t)));
+
+    m_models.erase(model);
+
+    if (m_models.empty()) {
+	if (m_converter) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+            cerr << "AudioCallbackPlaySource::removeModel: No models left, deleting SR converter" << endl;
+#endif
+	    src_delete(m_converter);
+	    m_converter = 0;
+	}
+	m_sourceSampleRate = 0;
+    }
+
+    sv_frame_t lastEnd = 0;
+    for (std::set<Model *>::const_iterator i = m_models.begin();
+	 i != m_models.end(); ++i) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+	cout << "AudioCallbackPlaySource::removeModel(" << model << "): checking end frame on model " << *i << endl;
+#endif
+	if ((*i)->getEndFrame() > lastEnd) {
+            lastEnd = (*i)->getEndFrame();
+        }
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+	cout << "(done, lastEnd now " << lastEnd << ")" << endl;
+#endif
+    }
+    m_lastModelEndFrame = lastEnd;
+
+    m_audioGenerator->removeModel(model);
+
+    m_mutex.unlock();
+
+    clearRingBuffers();
+}
+
+void
+AudioCallbackPlaySource::clearModels()
+{
+    m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource::clearModels()" << endl;
+#endif
+
+    m_models.clear();
+
+    if (m_converter) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+        cerr << "AudioCallbackPlaySource::clearModels: Deleting SR converter" << endl;
+#endif
+	src_delete(m_converter);
+	m_converter = 0;
+    }
+
+    m_lastModelEndFrame = 0;
+
+    m_sourceSampleRate = 0;
+
+    m_mutex.unlock();
+
+    m_audioGenerator->clearModels();
+
+    clearRingBuffers();
+}    
+
+void
+AudioCallbackPlaySource::clearRingBuffers(bool haveLock, int count)
+{
+    if (!haveLock) m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cerr << "clearRingBuffers" << endl;
+#endif
+
+    rebuildRangeLists();
+
+    if (count == 0) {
+	if (m_writeBuffers) count = int(m_writeBuffers->size());
+    }
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cerr << "current playing frame = " << getCurrentPlayingFrame() << endl;
+
+    cerr << "write buffer fill (before) = " << m_writeBufferFill << endl;
+#endif
+    
+    m_writeBufferFill = getCurrentBufferedFrame();
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cerr << "current buffered frame = " << m_writeBufferFill << endl;
+#endif
+
+    if (m_readBuffers != m_writeBuffers) {
+	delete m_writeBuffers;
+    }
+
+    m_writeBuffers = new RingBufferVector;
+
+    for (int i = 0; i < count; ++i) {
+	m_writeBuffers->push_back(new RingBuffer<float>(m_ringBufferSize));
+    }
+
+    m_audioGenerator->reset();
+    
+//    cout << "AudioCallbackPlaySource::clearRingBuffers: Created "
+//	      << count << " write buffers" << endl;
+
+    if (!haveLock) {
+	m_mutex.unlock();
+    }
+}
+
+void
+AudioCallbackPlaySource::play(sv_frame_t startFrame)
+{
+    if (!m_sourceSampleRate) {
+        cerr << "AudioCallbackPlaySource::play: No source sample rate available, not playing" << endl;
+        return;
+    }
+    
+    if (m_viewManager->getPlaySelectionMode() &&
+	!m_viewManager->getSelections().empty()) {
+
+        SVDEBUG << "AudioCallbackPlaySource::play: constraining frame " << startFrame << " to selection = ";
+
+        startFrame = m_viewManager->constrainFrameToSelection(startFrame);
+
+        SVDEBUG << startFrame << endl;
+
+    } else {
+        if (startFrame < 0) {
+            startFrame = 0;
+        }
+	if (startFrame >= m_lastModelEndFrame) {
+	    startFrame = 0;
+	}
+    }
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cerr << "play(" << startFrame << ") -> playback model ";
+#endif
+
+    startFrame = m_viewManager->alignReferenceToPlaybackFrame(startFrame);
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cerr << startFrame << endl;
+#endif
+
+    // 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.
+    // NO -- we can end up playing some first -- always reset here
+
+    m_mutex.lock();
+
+    if (m_timeStretcher) {
+        m_timeStretcher->reset();
+    }
+    if (m_monoStretcher) {
+        m_monoStretcher->reset();
+    }
+
+    m_readBufferFill = m_writeBufferFill = startFrame;
+    if (m_readBuffers) {
+        for (int c = 0; c < getTargetChannelCount(); ++c) {
+            RingBuffer<float> *rb = getReadRingBuffer(c);
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+            cerr << "reset ring buffer for channel " << c << endl;
+#endif
+            if (rb) rb->reset();
+        }
+    }
+    if (m_converter) src_reset(m_converter);
+
+    m_mutex.unlock();
+
+    m_audioGenerator->reset();
+
+    m_playStartFrame = startFrame;
+    m_playStartFramePassed = false;
+    m_playStartedAt = RealTime::zeroTime;
+    if (m_target) {
+        m_playStartedAt = RealTime::fromSeconds(m_target->getCurrentTime());
+    }
+
+    bool changed = !m_playing;
+    m_lastRetrievalTimestamp = 0;
+    m_lastCurrentFrame = 0;
+    m_playing = true;
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource::play: awakening thread" << endl;
+#endif
+
+    m_condition.wakeAll();
+    if (changed) {
+        emit playStatusChanged(m_playing);
+        emit activity(tr("Play from %1").arg
+                      (RealTime::frame2RealTime
+                       (m_playStartFrame, m_sourceSampleRate).toText().c_str()));
+    }
+}
+
+void
+AudioCallbackPlaySource::stop()
+{
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    SVDEBUG << "AudioCallbackPlaySource::stop()" << endl;
+#endif
+    bool changed = m_playing;
+    m_playing = false;
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource::stop: awakening thread" << endl;
+#endif
+
+    m_condition.wakeAll();
+    m_lastRetrievalTimestamp = 0;
+    if (changed) {
+        emit playStatusChanged(m_playing);
+        emit activity(tr("Stop at %1").arg
+                      (RealTime::frame2RealTime
+                       (m_lastCurrentFrame, m_sourceSampleRate).toText().c_str()));
+    }
+    m_lastCurrentFrame = 0;
+}
+
+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()
+{
+    cerr << "Audio processing overload!" << endl;
+
+    if (!m_playing) return;
+
+    RealTimePluginInstance *ap = m_auditioningPlugin;
+    if (ap && !m_auditioningPluginBypassed) {
+        m_auditioningPluginBypassed = true;
+        emit audioOverloadPluginDisabled();
+        return;
+    }
+
+    if (m_timeStretcher &&
+        m_timeStretcher->getTimeRatio() < 1.0 &&
+        m_stretcherInputCount > 1 &&
+        m_monoStretcher && !m_stretchMono) {
+        m_stretchMono = true;
+        emit audioTimeStretchMultiChannelDisabled();
+        return;
+    }
+}
+
+void
+AudioCallbackPlaySource::setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *target)
+{
+    m_target = target;
+}
+
+void
+AudioCallbackPlaySource::setSystemPlaybackBlockSize(int size)
+{
+    cout << "AudioCallbackPlaySource::setTarget: Block size -> " << size << endl;
+    if (size != 0) {
+        m_blockSize = size;
+    }
+    if (size * 4 > m_ringBufferSize) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+        cerr << "AudioCallbackPlaySource::setTarget: Buffer size "
+             << size << " > a quarter of ring buffer size "
+             << m_ringBufferSize << ", calling for more ring buffer"
+             << endl;
+#endif
+        m_ringBufferSize = size * 4;
+        if (m_writeBuffers && !m_writeBuffers->empty()) {
+            clearRingBuffers();
+        }
+    }
+}
+
+int
+AudioCallbackPlaySource::getTargetBlockSize() const
+{
+//    cout << "AudioCallbackPlaySource::getTargetBlockSize() -> " << m_blockSize << endl;
+    return int(m_blockSize);
+}
+
+void
+AudioCallbackPlaySource::setSystemPlaybackLatency(int latency)
+{
+    m_playLatency = latency;
+}
+
+sv_frame_t
+AudioCallbackPlaySource::getTargetPlayLatency() const
+{
+    return m_playLatency;
+}
+
+sv_frame_t
+AudioCallbackPlaySource::getCurrentPlayingFrame()
+{
+    // This method attempts to estimate which audio sample frame is
+    // "currently coming through the speakers".
+
+    sv_samplerate_t targetRate = getTargetSampleRate();
+    sv_frame_t latency = m_playLatency; // at target rate
+    RealTime latency_t = RealTime::zeroTime;
+
+    if (targetRate != 0) {
+        latency_t = RealTime::frame2RealTime(latency, targetRate);
+    }
+
+    return getCurrentFrame(latency_t);
+}
+
+sv_frame_t
+AudioCallbackPlaySource::getCurrentBufferedFrame()
+{
+    return getCurrentFrame(RealTime::zeroTime);
+}
+
+sv_frame_t
+AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t)
+{
+    // We resample when filling the ring buffer, and time-stretch when
+    // draining it.  The buffer contains data at the "target rate" and
+    // the latency provided by the target is also at the target rate.
+    // Because of the multiple rates involved, we do the actual
+    // calculation using RealTime instead.
+
+    sv_samplerate_t sourceRate = getSourceSampleRate();
+    sv_samplerate_t targetRate = getTargetSampleRate();
+
+    if (sourceRate == 0 || targetRate == 0) return 0;
+
+    int inbuffer = 0; // at target rate
+
+    for (int c = 0; c < getTargetChannelCount(); ++c) {
+	RingBuffer<float> *rb = getReadRingBuffer(c);
+	if (rb) {
+	    int here = rb->getReadSpace();
+	    if (c == 0 || here < inbuffer) inbuffer = here;
+	}
+    }
+
+    sv_frame_t readBufferFill = m_readBufferFill;
+    sv_frame_t lastRetrievedBlockSize = m_lastRetrievedBlockSize;
+    double lastRetrievalTimestamp = m_lastRetrievalTimestamp;
+    double currentTime = 0.0;
+    if (m_target) currentTime = m_target->getCurrentTime();
+
+    bool looping = m_viewManager->getPlayLoopMode();
+
+    RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate);
+
+    sv_frame_t stretchlat = 0;
+    double timeRatio = 1.0;
+
+    if (m_timeStretcher) {
+        stretchlat = m_timeStretcher->getLatency();
+        timeRatio = m_timeStretcher->getTimeRatio();
+    }
+
+    RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, targetRate);
+
+    // When the target has just requested a block from us, the last
+    // sample it obtained was our buffer fill frame count minus the
+    // amount of read space (converted back to source sample rate)
+    // remaining now.  That sample is not expected to be played until
+    // the target's play latency has elapsed.  By the time the
+    // following block is requested, that sample will be at the
+    // target's play latency minus the last requested block size away
+    // from being played.
+
+    RealTime sincerequest_t = RealTime::zeroTime;
+    RealTime lastretrieved_t = RealTime::zeroTime;
+
+    if (m_target &&
+        m_trustworthyTimestamps &&
+        lastRetrievalTimestamp != 0.0) {
+
+        lastretrieved_t = RealTime::frame2RealTime
+            (lastRetrievedBlockSize, targetRate);
+
+        // calculate number of frames at target rate that have elapsed
+        // since the end of the last call to getSourceSamples
+
+        if (m_trustworthyTimestamps && !looping) {
+
+            // this adjustment seems to cause more problems when looping
+            double elapsed = currentTime - lastRetrievalTimestamp;
+
+            if (elapsed > 0.0) {
+                sincerequest_t = RealTime::fromSeconds(elapsed);
+            }
+        }
+
+    } else {
+
+        lastretrieved_t = RealTime::frame2RealTime
+            (getTargetBlockSize(), targetRate);
+    }
+
+    RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, sourceRate);
+
+    if (timeRatio != 1.0) {
+        lastretrieved_t = lastretrieved_t / timeRatio;
+        sincerequest_t = sincerequest_t / timeRatio;
+        latency_t = latency_t / timeRatio;
+    }
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+    cerr << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", time ratio " << timeRatio << "\n  stretcher latency: " << stretchlat_t << ", device latency: " << latency_t << "\n  since request: " << sincerequest_t << ", last retrieved quantity: " << lastretrieved_t << endl;
+#endif
+
+    // Normally the range lists should contain at least one item each
+    // -- if playback is unconstrained, that item should report the
+    // entire source audio duration.
+
+    if (m_rangeStarts.empty()) {
+        rebuildRangeLists();
+    }
+
+    if (m_rangeStarts.empty()) {
+        // this code is only used in case of error in rebuildRangeLists
+        RealTime playing_t = bufferedto_t
+            - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t
+            + sincerequest_t;
+        if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime;
+        sv_frame_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
+        return m_viewManager->alignPlaybackFrameToReference(frame);
+    }
+
+    int inRange = 0;
+    int index = 0;
+
+    for (int i = 0; i < (int)m_rangeStarts.size(); ++i) {
+        if (bufferedto_t >= m_rangeStarts[i]) {
+            inRange = index;
+        } else {
+            break;
+        }
+        ++index;
+    }
+
+    if (inRange >= int(m_rangeStarts.size())) {
+        inRange = int(m_rangeStarts.size())-1;
+    }
+
+    RealTime playing_t = bufferedto_t;
+
+    playing_t = playing_t
+        - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t
+        + sincerequest_t;
+
+    // This rather gross little hack is used to ensure that latency
+    // compensation doesn't result in the playback pointer appearing
+    // to start earlier than the actual playback does.  It doesn't
+    // work properly (hence the bail-out in the middle) because if we
+    // are playing a relatively short looped region, the playing time
+    // estimated from the buffer fill frame may have wrapped around
+    // the region boundary and end up being much smaller than the
+    // theoretical play start frame, perhaps even for the entire
+    // duration of playback!
+
+    if (!m_playStartFramePassed) {
+        RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame,
+                                                        sourceRate);
+        if (playing_t < playstart_t) {
+//            cerr << "playing_t " << playing_t << " < playstart_t " 
+//                      << playstart_t << endl;
+            if (/*!!! sincerequest_t > RealTime::zeroTime && */
+                m_playStartedAt + latency_t + stretchlat_t <
+                RealTime::fromSeconds(currentTime)) {
+//                cerr << "but we've been playing for long enough that I think we should disregard it (it probably results from loop wrapping)" << endl;
+                m_playStartFramePassed = true;
+            } else {
+                playing_t = playstart_t;
+            }
+        } else {
+            m_playStartFramePassed = true;
+        }
+    }
+ 
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+    cerr << "playing_t " << playing_t;
+#endif
+
+    playing_t = playing_t - m_rangeStarts[inRange];
+ 
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+    cerr << " as offset into range " << inRange << " (start =" << m_rangeStarts[inRange] << " duration =" << m_rangeDurations[inRange] << ") = " << playing_t << endl;
+#endif
+
+    while (playing_t < RealTime::zeroTime) {
+
+        if (inRange == 0) {
+            if (looping) {
+                inRange = int(m_rangeStarts.size()) - 1;
+            } else {
+                break;
+            }
+        } else {
+            --inRange;
+        }
+
+        playing_t = playing_t + m_rangeDurations[inRange];
+    }
+
+    playing_t = playing_t + m_rangeStarts[inRange];
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+    cerr << "  playing time: " << playing_t << endl;
+#endif
+
+    if (!looping) {
+        if (inRange == (int)m_rangeStarts.size()-1 &&
+            playing_t >= m_rangeStarts[inRange] + m_rangeDurations[inRange]) {
+cerr << "Not looping, inRange " << inRange << " == rangeStarts.size()-1, playing_t " << playing_t << " >= m_rangeStarts[inRange] " << m_rangeStarts[inRange] << " + m_rangeDurations[inRange] " << m_rangeDurations[inRange] << " -- stopping" << endl;
+            stop();
+        }
+    }
+
+    if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime;
+
+    sv_frame_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
+
+    if (m_lastCurrentFrame > 0 && !looping) {
+        if (frame < m_lastCurrentFrame) {
+            frame = m_lastCurrentFrame;
+        }
+    }
+
+    m_lastCurrentFrame = frame;
+
+    return m_viewManager->alignPlaybackFrameToReference(frame);
+}
+
+void
+AudioCallbackPlaySource::rebuildRangeLists()
+{
+    bool constrained = (m_viewManager->getPlaySelectionMode());
+
+    m_rangeStarts.clear();
+    m_rangeDurations.clear();
+
+    sv_samplerate_t sourceRate = getSourceSampleRate();
+    if (sourceRate == 0) return;
+
+    RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate);
+    if (end == RealTime::zeroTime) return;
+
+    if (!constrained) {
+        m_rangeStarts.push_back(RealTime::zeroTime);
+        m_rangeDurations.push_back(end);
+        return;
+    }
+
+    MultiSelection::SelectionList selections = m_viewManager->getSelections();
+    MultiSelection::SelectionList::const_iterator i;
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    SVDEBUG << "AudioCallbackPlaySource::rebuildRangeLists" << endl;
+#endif
+
+    if (!selections.empty()) {
+
+        for (i = selections.begin(); i != selections.end(); ++i) {
+            
+            RealTime start =
+                (RealTime::frame2RealTime
+                 (m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()),
+                  sourceRate));
+            RealTime duration = 
+                (RealTime::frame2RealTime
+                 (m_viewManager->alignReferenceToPlaybackFrame(i->getEndFrame()) -
+                  m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()),
+                  sourceRate));
+            
+            m_rangeStarts.push_back(start);
+            m_rangeDurations.push_back(duration);
+        }
+    } else {
+        m_rangeStarts.push_back(RealTime::zeroTime);
+        m_rangeDurations.push_back(end);
+    }
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cerr << "Now have " << m_rangeStarts.size() << " play ranges" << endl;
+#endif
+}
+
+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::setSystemPlaybackSampleRate(int sr)
+{
+    bool first = (m_targetSampleRate == 0);
+
+    m_targetSampleRate = sr;
+    initialiseConverter();
+
+    if (first && (m_stretchRatio != 1.f)) {
+        // couldn't create a stretcher before because we had no sample
+        // rate: make one now
+        setTimeStretch(m_stretchRatio);
+    }
+}
+
+void
+AudioCallbackPlaySource::initialiseConverter()
+{
+    m_mutex.lock();
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cerr << "AudioCallbackPlaySource::initialiseConverter(): from "
+         << getSourceSampleRate() << " to " << getTargetSampleRate() << endl;
+#endif
+    
+    if (m_converter) {
+        src_delete(m_converter);
+        m_converter = 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) {
+	    cerr << "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: "
+                 << src_strerror(err) << endl;
+
+            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
+    SVDEBUG << "AudioCallbackPlaySource::setResampleQuality: setting to "
+              << m_resampleQuality << endl;
+#endif
+
+    initialiseConverter();
+}
+
+void
+AudioCallbackPlaySource::setAuditioningEffect(Auditionable *a)
+{
+    RealTimePluginInstance *plugin = dynamic_cast<RealTimePluginInstance *>(a);
+    if (a && !plugin) {
+        cerr << "WARNING: AudioCallbackPlaySource::setAuditioningEffect: auditionable object " << a << " is not a real-time plugin instance" << endl;
+    }
+
+    m_mutex.lock();
+    m_auditioningPlugin = plugin;
+    m_auditioningPluginBypassed = false;
+    m_mutex.unlock();
+}
+
+void
+AudioCallbackPlaySource::setSoloModelSet(std::set<Model *> s)
+{
+    m_audioGenerator->setSoloModelSet(s);
+    clearRingBuffers();
+}
+
+void
+AudioCallbackPlaySource::clearSoloModelSet()
+{
+    m_audioGenerator->clearSoloModelSet();
+    clearRingBuffers();
+}
+
+sv_samplerate_t
+AudioCallbackPlaySource::getTargetSampleRate() const
+{
+    if (m_targetSampleRate) return m_targetSampleRate;
+    else return getSourceSampleRate();
+}
+
+int
+AudioCallbackPlaySource::getSourceChannelCount() const
+{
+    return m_sourceChannelCount;
+}
+
+int
+AudioCallbackPlaySource::getTargetChannelCount() const
+{
+    if (m_sourceChannelCount < 2) return 2;
+    return m_sourceChannelCount;
+}
+
+sv_samplerate_t
+AudioCallbackPlaySource::getSourceSampleRate() const
+{
+    return m_sourceSampleRate;
+}
+
+void
+AudioCallbackPlaySource::setTimeStretch(double factor)
+{
+    m_stretchRatio = factor;
+
+    if (!getTargetSampleRate()) return; // have to make our stretcher later
+
+    if (m_timeStretcher || (factor == 1.0)) {
+        // stretch ratio will be set in next process call if appropriate
+    } else {
+        m_stretcherInputCount = getTargetChannelCount();
+        RubberBandStretcher *stretcher = new RubberBandStretcher
+            (int(getTargetSampleRate()),
+             m_stretcherInputCount,
+             RubberBandStretcher::OptionProcessRealTime,
+             factor);
+        RubberBandStretcher *monoStretcher = new RubberBandStretcher
+            (int(getTargetSampleRate()),
+             1,
+             RubberBandStretcher::OptionProcessRealTime,
+             factor);
+        m_stretcherInputs = new float *[m_stretcherInputCount];
+        m_stretcherInputSizes = new sv_frame_t[m_stretcherInputCount];
+        for (int c = 0; c < m_stretcherInputCount; ++c) {
+            m_stretcherInputSizes[c] = 16384;
+            m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]];
+        }
+        m_monoStretcher = monoStretcher;
+        m_timeStretcher = stretcher;
+    }
+
+    emit activity(tr("Change time-stretch factor to %1").arg(factor));
+}
+
+int
+AudioCallbackPlaySource::getSourceSamples(int count, float **buffer)
+{
+    if (!m_playing) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+        SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Not playing" << endl;
+#endif
+	for (int ch = 0; ch < getTargetChannelCount(); ++ch) {
+	    for (int i = 0; i < count; ++i) {
+		buffer[ch][i] = 0.0;
+	    }
+	}
+	return 0;
+    }
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+    SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Playing" << endl;
+#endif
+
+    // Ensure that all buffers have at least the amount of data we
+    // need -- else reduce the size of our requests correspondingly
+
+    for (int ch = 0; ch < getTargetChannelCount(); ++ch) {
+
+        RingBuffer<float> *rb = getReadRingBuffer(ch);
+        
+        if (!rb) {
+            cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: "
+                      << "No ring buffer available for channel " << ch
+                      << ", returning no data here" << endl;
+            count = 0;
+            break;
+        }
+
+        int rs = rb->getReadSpace();
+        if (rs < count) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+            cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: "
+                      << "Ring buffer for channel " << ch << " has only "
+                      << rs << " (of " << count << ") samples available ("
+                      << "ring buffer size is " << rb->getSize() << ", write "
+                      << "space " << rb->getWriteSpace() << "), "
+                      << "reducing request size" << endl;
+#endif
+            count = rs;
+        }
+    }
+
+    if (count == 0) return 0;
+
+    RubberBandStretcher *ts = m_timeStretcher;
+    RubberBandStretcher *ms = m_monoStretcher;
+
+    double ratio = ts ? ts->getTimeRatio() : 1.0;
+
+    if (ratio != m_stretchRatio) {
+        if (!ts) {
+            cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: Time ratio change to " << m_stretchRatio << " is pending, but no stretcher is set" << endl;
+            m_stretchRatio = 1.0;
+        } else {
+            ts->setTimeRatio(m_stretchRatio);
+            if (ms) ms->setTimeRatio(m_stretchRatio);
+            if (m_stretchRatio >= 1.0) m_stretchMono = false;
+        }
+    }
+
+    int stretchChannels = m_stretcherInputCount;
+    if (m_stretchMono) {
+        if (ms) {
+            ts = ms;
+            stretchChannels = 1;
+        } else {
+            m_stretchMono = false;
+        }
+    }
+
+    if (m_target) {
+        m_lastRetrievedBlockSize = count;
+        m_lastRetrievalTimestamp = m_target->getCurrentTime();
+    }
+
+    if (!ts || ratio == 1.f) {
+
+	int got = 0;
+
+	for (int 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":
+		sv_frame_t request = count;
+		if (ch > 0) request = got;
+
+		got = rb->read(buffer[ch], int(request));
+	    
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+		cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl;
+#endif
+	    }
+
+	    for (int ch = 0; ch < getTargetChannelCount(); ++ch) {
+		for (int i = got; i < count; ++i) {
+		    buffer[ch][i] = 0.0;
+		}
+	    }
+	}
+
+        applyAuditioningEffect(count, buffer);
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource::getSamples: awakening thread" << endl;
+#endif
+
+        m_condition.wakeAll();
+
+	return got;
+    }
+
+    int channels = getTargetChannelCount();
+    sv_frame_t available;
+    sv_frame_t fedToStretcher = 0;
+    int warned = 0;
+
+    // The input block for a given output is approx output / ratio,
+    // but we can't predict it exactly, for an adaptive timestretcher.
+
+    while ((available = ts->available()) < count) {
+
+        sv_frame_t reqd = lrint(double(count - available) / ratio);
+        reqd = std::max(reqd, sv_frame_t(ts->getSamplesRequired()));
+        if (reqd == 0) reqd = 1;
+                
+        sv_frame_t got = reqd;
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+        cerr << "reqd = " <<reqd << ", channels = " << channels << ", ic = " << m_stretcherInputCount << endl;
+#endif
+
+        for (int c = 0; c < channels; ++c) {
+            if (c >= m_stretcherInputCount) continue;
+            if (reqd > m_stretcherInputSizes[c]) {
+                if (c == 0) {
+                    cerr << "WARNING: resizing stretcher input buffer from " << m_stretcherInputSizes[c] << " to " << (reqd * 2) << endl;
+                }
+                delete[] m_stretcherInputs[c];
+                m_stretcherInputSizes[c] = reqd * 2;
+                m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]];
+            }
+        }
+
+        for (int c = 0; c < channels; ++c) {
+            if (c >= m_stretcherInputCount) continue;
+            RingBuffer<float> *rb = getReadRingBuffer(c);
+            if (rb) {
+                sv_frame_t gotHere;
+                if (stretchChannels == 1 && c > 0) {
+                    gotHere = rb->readAdding(m_stretcherInputs[0], int(got));
+                } else {
+                    gotHere = rb->read(m_stretcherInputs[c], int(got));
+                }
+                if (gotHere < got) got = gotHere;
+                
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+                if (c == 0) {
+                    SVDEBUG << "feeding stretcher: got " << gotHere
+                              << ", " << rb->getReadSpace() << " remain" << endl;
+                }
+#endif
+                
+            } else {
+                cerr << "WARNING: No ring buffer available for channel " << c << " in stretcher input block" << endl;
+            }
+        }
+
+        if (got < reqd) {
+            cerr << "WARNING: Read underrun in playback ("
+                      << got << " < " << reqd << ")" << endl;
+        }
+
+        ts->process(m_stretcherInputs, size_t(got), false);
+
+        fedToStretcher += got;
+
+        if (got == 0) break;
+
+        if (ts->available() == available) {
+            cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << endl;
+            if (++warned == 5) break;
+        }
+    }
+
+    ts->retrieve(buffer, size_t(count));
+
+    for (int c = stretchChannels; c < getTargetChannelCount(); ++c) {
+        for (int i = 0; i < count; ++i) {
+            buffer[c][i] = buffer[0][i];
+        }
+    }
+
+    applyAuditioningEffect(count, buffer);
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySource::getSamples [stretched]: awakening thread" << endl;
+#endif
+
+    m_condition.wakeAll();
+
+    return count;
+}
+
+void
+AudioCallbackPlaySource::applyAuditioningEffect(sv_frame_t count, float **buffers)
+{
+    if (m_auditioningPluginBypassed) return;
+    RealTimePluginInstance *plugin = m_auditioningPlugin;
+    if (!plugin) return;
+    
+    if ((int)plugin->getAudioInputCount() != getTargetChannelCount()) {
+//        cerr << "plugin input count " << plugin->getAudioInputCount() 
+//                  << " != our channel count " << getTargetChannelCount()
+//                  << endl;
+        return;
+    }
+    if ((int)plugin->getAudioOutputCount() != getTargetChannelCount()) {
+//        cerr << "plugin output count " << plugin->getAudioOutputCount() 
+//                  << " != our channel count " << getTargetChannelCount()
+//                  << endl;
+        return;
+    }
+    if ((int)plugin->getBufferSize() < count) {
+//        cerr << "plugin buffer size " << plugin->getBufferSize() 
+//                  << " < our block size " << count
+//                  << endl;
+        return;
+    }
+
+    float **ib = plugin->getAudioInputBuffers();
+    float **ob = plugin->getAudioOutputBuffers();
+
+    for (int c = 0; c < getTargetChannelCount(); ++c) {
+        for (int i = 0; i < count; ++i) {
+            ib[c][i] = buffers[c][i];
+        }
+    }
+
+    plugin->run(Vamp::RealTime::zeroTime, int(count));
+    
+    for (int c = 0; c < getTargetChannelCount(); ++c) {
+        for (int 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 sv_frame_t tmpSize = 0;
+
+    sv_frame_t space = 0;
+    for (int c = 0; c < getTargetChannelCount(); ++c) {
+	RingBuffer<float> *wb = getWriteRingBuffer(c);
+	if (wb) {
+	    sv_frame_t spaceHere = wb->getWriteSpace();
+	    if (c == 0 || spaceHere < space) space = spaceHere;
+	}
+    }
+    
+    if (space == 0) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+        cout << "AudioCallbackPlaySourceFillThread: no space to fill" << endl;
+#endif
+        return false;
+    }
+
+    sv_frame_t f = m_writeBufferFill;
+	
+    bool readWriteEqual = (m_readBuffers == m_writeBuffers);
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    if (!readWriteEqual) {
+        cout << "AudioCallbackPlaySourceFillThread: note read buffers != write buffers" << endl;
+    }
+    cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << endl;
+#endif
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "buffered to " << f << " already" << endl;
+#endif
+
+    bool resample = (getSourceSampleRate() != getTargetSampleRate());
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << (resample ? "" : "not ") << "resampling (source " << getSourceSampleRate() << ", target " << getTargetSampleRate() << ")" << endl;
+#endif
+
+    int channels = getTargetChannelCount();
+
+    sv_frame_t orig = space;
+    sv_frame_t got = 0;
+
+    static float **bufferPtrs = 0;
+    static int bufferPtrCount = 0;
+
+    if (bufferPtrCount < channels) {
+	if (bufferPtrs) delete[] bufferPtrs;
+	bufferPtrs = new float *[channels];
+	bufferPtrCount = channels;
+    }
+
+    sv_frame_t generatorBlockSize = m_audioGenerator->getBlockSize();
+
+    if (resample && !m_converter) {
+        throw std::logic_error("Sample rates differ, but no converter available!");
+    }
+
+    if (resample && m_converter) {
+
+	double ratio =
+	    double(getTargetSampleRate()) / double(getSourceSampleRate());
+	orig = sv_frame_t(double(orig) / ratio + 0.1);
+
+	// orig must be a multiple of generatorBlockSize
+	orig = (orig / generatorBlockSize) * generatorBlockSize;
+	if (orig == 0) return false;
+
+	sv_frame_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 (int c = 0; c < channels; ++c) {
+	    for (int i = 0; i < orig; ++i) {
+		nonintlv[channels * i + c] = 0.0f;
+	    }
+	}
+
+	for (int c = 0; c < channels; ++c) {
+	    bufferPtrs[c] = nonintlv + c * orig;
+	}
+
+	got = mixModels(f, orig, bufferPtrs); // also modifies f
+
+	// and interleave into first half
+	for (int c = 0; c < channels; ++c) {
+	    for (int 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 = long(got);
+	data.output_frames = long(work);
+	data.src_ratio = ratio;
+	data.end_of_input = 0;
+	
+	int err = src_process(m_converter, &data);
+
+	sv_frame_t toCopy = sv_frame_t(double(got) * ratio + 0.1);
+
+	if (err) {
+	    cerr
+		<< "AudioCallbackPlaySourceFillThread: ERROR in samplerate conversion: "
+		<< src_strerror(err) << endl;
+	    //!!! Then what?
+	} else {
+	    got = data.input_frames_used;
+	    toCopy = data.output_frames_gen;
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+	    cout << "Resampled " << got << " frames to " << toCopy << " frames" << endl;
+#endif
+	}
+	
+	for (int c = 0; c < channels; ++c) {
+	    for (int i = 0; i < toCopy; ++i) {
+		tmp[i] = srcout[channels * i + c];
+	    }
+	    RingBuffer<float> *wb = getWriteRingBuffer(c);
+	    if (wb) wb->write(tmp, int(toCopy));
+	}
+
+	m_writeBufferFill = f;
+	if (readWriteEqual) m_readBufferFill = f;
+
+    } else {
+
+	// space must be a multiple of generatorBlockSize
+        sv_frame_t reqSpace = space;
+	space = (reqSpace / generatorBlockSize) * generatorBlockSize;
+	if (space == 0) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+            cout << "requested fill of " << reqSpace
+                      << " is less than generator block size of "
+                      << generatorBlockSize << ", leaving it" << endl;
+#endif
+            return false;
+        }
+
+	if (tmpSize < channels * space) {
+	    delete[] tmp;
+	    tmp = new float[channels * space];
+	    tmpSize = channels * space;
+	}
+
+	for (int c = 0; c < channels; ++c) {
+
+	    bufferPtrs[c] = tmp + c * space;
+	    
+	    for (int i = 0; i < space; ++i) {
+		tmp[c * space + i] = 0.0f;
+	    }
+	}
+
+	sv_frame_t got = mixModels(f, space, bufferPtrs); // also modifies f
+
+	for (int c = 0; c < channels; ++c) {
+
+	    RingBuffer<float> *wb = getWriteRingBuffer(c);
+	    if (wb) {
+                int actual = wb->write(bufferPtrs[c], int(got));
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+		cout << "Wrote " << actual << " samples for ch " << c << ", now "
+			  << wb->getReadSpace() << " to read" 
+			  << endl;
+#endif
+                if (actual < got) {
+                    cerr << "WARNING: Buffer overrun in channel " << c
+                              << ": wrote " << actual << " of " << got
+                              << " samples" << endl;
+                }
+            }
+	}
+
+	m_writeBufferFill = f;
+	if (readWriteEqual) m_readBufferFill = f;
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+        cout << "Read buffer fill is now " << m_readBufferFill << endl;
+#endif
+
+	//!!! 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;
+}    
+
+sv_frame_t
+AudioCallbackPlaySource::mixModels(sv_frame_t &frame, sv_frame_t count, float **buffers)
+{
+    sv_frame_t processed = 0;
+    sv_frame_t chunkStart = frame;
+    sv_frame_t chunkSize = count;
+    sv_frame_t selectionSize = 0;
+    sv_frame_t nextChunkStart = chunkStart + chunkSize;
+    
+    bool looping = m_viewManager->getPlayLoopMode();
+    bool constrained = (m_viewManager->getPlaySelectionMode() &&
+			!m_viewManager->getSelections().empty());
+
+    static float **chunkBufferPtrs = 0;
+    static int chunkBufferPtrCount = 0;
+    int channels = getTargetChannelCount();
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << endl;
+#endif
+
+    if (chunkBufferPtrCount < channels) {
+	if (chunkBufferPtrs) delete[] chunkBufferPtrs;
+	chunkBufferPtrs = new float *[channels];
+	chunkBufferPtrCount = channels;
+    }
+
+    for (int c = 0; c < channels; ++c) {
+	chunkBufferPtrs[c] = buffers[c];
+    }
+
+    while (processed < count) {
+	
+	chunkSize = count - processed;
+	nextChunkStart = chunkStart + chunkSize;
+	selectionSize = 0;
+
+	sv_frame_t fadeIn = 0, fadeOut = 0;
+
+	if (constrained) {
+
+            sv_frame_t rChunkStart =
+                m_viewManager->alignPlaybackFrameToReference(chunkStart);
+	    
+	    Selection selection =
+		m_viewManager->getContainingSelection(rChunkStart, true);
+	    
+	    if (selection.isEmpty()) {
+		if (looping) {
+		    selection = *m_viewManager->getSelections().begin();
+		    chunkStart = m_viewManager->alignReferenceToPlaybackFrame
+                        (selection.getStartFrame());
+		    fadeIn = 50;
+		}
+	    }
+
+	    if (selection.isEmpty()) {
+
+		chunkSize = 0;
+		nextChunkStart = chunkStart;
+
+	    } else {
+
+                sv_frame_t sf = m_viewManager->alignReferenceToPlaybackFrame
+                    (selection.getStartFrame());
+                sv_frame_t ef = m_viewManager->alignReferenceToPlaybackFrame
+                    (selection.getEndFrame());
+
+		selectionSize = ef - sf;
+
+		if (chunkStart < sf) {
+		    chunkStart = sf;
+		    fadeIn = 50;
+		}
+
+		nextChunkStart = chunkStart + chunkSize;
+
+		if (nextChunkStart >= ef) {
+		    nextChunkStart = ef;
+		    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;
+	}
+	
+//	cout << "chunkStart " << chunkStart << ", chunkSize " << chunkSize << ", nextChunkStart " << nextChunkStart << ", frame " << frame << ", count " << count << ", processed " << processed << endl;
+
+	if (!chunkSize) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+	    cout << "Ending selection playback at " << nextChunkStart << 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
+	cout << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << endl;
+#endif
+
+	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) {
+	    
+	    (void) m_audioGenerator->mixModel(*mi, chunkStart, 
+                                              chunkSize, chunkBufferPtrs,
+                                              fadeIn, fadeOut);
+	}
+
+	for (int c = 0; c < channels; ++c) {
+	    chunkBufferPtrs[c] += chunkSize;
+	}
+
+	processed += chunkSize;
+	chunkStart = nextChunkStart;
+    }
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "Returning selection playback " << processed << " frames to " << nextChunkStart << 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 (int 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
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+                    SVDEBUG << "AudioCallbackPlaySource::unifyRingBuffers: Not unifying: write buffer has less (" << wb->getReadSpace() << ") than " << m_blockSize*2 << " to read and write buffer fill (" << m_writeBufferFill << ") is not close to end frame (" << m_lastModelEndFrame << ")" << endl;
+#endif
+		    return;
+		}
+	    }
+	    break;
+	}
+    }
+
+    sv_frame_t rf = m_readBufferFill;
+    RingBuffer<float> *rb = getReadRingBuffer(0);
+    if (rb) {
+	int rs = rb->getReadSpace();
+	//!!! incorrect when in non-contiguous selection, see comments elsewhere
+//	cout << "rs = " << rs << endl;
+	if (rs < rf) rf -= rs;
+	else rf = 0;
+    }
+    
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+    SVDEBUG << "AudioCallbackPlaySource::unifyRingBuffers: m_readBufferFill = " << m_readBufferFill << ", rf = " << rf << ", m_writeBufferFill = " << m_writeBufferFill << endl;
+#endif
+
+    sv_frame_t wf = m_writeBufferFill;
+    sv_frame_t skip = 0;
+    for (int c = 0; c < getTargetChannelCount(); ++c) {
+	RingBuffer<float> *wb = getWriteRingBuffer(c);
+	if (wb) {
+	    if (c == 0) {
+		
+		int wrs = wb->getReadSpace();
+//		cout << "wrs = " << wrs << endl;
+
+		if (wrs < wf) wf -= wrs;
+		else wf = 0;
+//		cout << "wf = " << wf << endl;
+		
+		if (wf < rf) skip = rf - wf;
+		if (skip == 0) break;
+	    }
+
+//	    cout << "skipping " << skip << endl;
+	    wb->skip(int(skip));
+	}
+    }
+		    
+    m_bufferScavenger.claim(m_readBuffers);
+    m_readBuffers = m_writeBuffers;
+    m_readBufferFill = m_writeBufferFill;
+#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
+    cerr << "unified" << endl;
+#endif
+}
+
+void
+AudioCallbackPlaySource::FillThread::run()
+{
+    AudioCallbackPlaySource &s(m_source);
+    
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+    cout << "AudioCallbackPlaySourceFillThread starting" << 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();
+
+	if (work && s.m_playing && s.getSourceSampleRate()) {
+	    
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+	    cout << "AudioCallbackPlaySourceFillThread: not waiting" << endl;
+#endif
+
+	    s.m_mutex.unlock();
+	    s.m_mutex.lock();
+
+	} else {
+	    
+	    double ms = 100;
+	    if (s.getSourceSampleRate() > 0) {
+		ms = double(s.m_ringBufferSize) / s.getSourceSampleRate() * 1000.0;
+	    }
+	    
+	    if (s.m_playing) ms /= 10;
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+            if (!s.m_playing) cout << endl;
+	    cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << endl;
+#endif
+	    
+	    s.m_condition.wait(&s.m_mutex, int(ms));
+	}
+
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+	cout << "AudioCallbackPlaySourceFillThread: awoken" << endl;
+#endif
+
+	work = false;
+
+	if (!s.getSourceSampleRate()) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+            cout << "AudioCallbackPlaySourceFillThread: source sample rate is zero" << endl;
+#endif
+            continue;
+        }
+
+	bool playing = s.m_playing;
+
+	if (playing && !previouslyPlaying) {
+#ifdef DEBUG_AUDIO_PLAY_SOURCE
+	    cout << "AudioCallbackPlaySourceFillThread: playback state changed, resetting" << endl;
+#endif
+	    for (int 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();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/AudioCallbackPlaySource.h	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,405 @@
+/* -*- 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 <bqaudioio/ApplicationPlaybackSource.h>
+
+#include <QObject>
+#include <QMutex>
+#include <QWaitCondition>
+
+#include "base/Thread.h"
+#include "base/RealTime.h"
+
+#include <samplerate.h>
+
+#include <set>
+#include <map>
+
+namespace RubberBand {
+    class RubberBandStretcher;
+}
+
+class Model;
+class ViewManagerBase;
+class AudioGenerator;
+class PlayParameters;
+class RealTimePluginInstance;
+class AudioCallbackPlayTarget;
+
+/**
+ * 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 QObject,
+				public AudioPlaySource,
+                                public breakfastquay::ApplicationPlaybackSource
+{
+    Q_OBJECT
+
+public:
+    AudioCallbackPlaySource(ViewManagerBase *, QString clientName);
+    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(sv_frame_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 sv_frame_t getCurrentPlayingFrame();
+    
+    /** 
+     * Return the last frame that would come out of the speakers if we
+     * stopped playback right now.
+     */
+    virtual sv_frame_t getCurrentBufferedFrame();
+
+    /**
+     * Return the frame at which playback is expected to end (if not looping).
+     */
+    virtual sv_frame_t getPlayEndFrame() { return m_lastModelEndFrame; }
+
+    /**
+     * Set the playback target.
+     */
+    virtual void setSystemPlaybackTarget(breakfastquay::SystemPlaybackTarget *);
+
+    /**
+     * Set the block size of the target audio device.  This should be
+     * called by the target class.
+     */
+    virtual void setSystemPlaybackBlockSize(int blockSize);
+
+    /**
+     * Get the block size of the target audio device.  This may be an
+     * estimate or upper bound, if the target has a variable block
+     * size; the source should behave itself even if this value turns
+     * out to be inaccurate.
+     */
+    int 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 setSystemPlaybackLatency(int);
+
+    /**
+     * Get the playback latency of the target audio device.
+     */
+    sv_frame_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 setSystemPlaybackSampleRate(int);
+
+    /**
+     * Return the sample rate set by the target audio device (or the
+     * source sample rate if the target hasn't set one).
+     */
+    virtual sv_samplerate_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.
+     */
+    int 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.
+     */
+    int getTargetChannelCount() const;
+
+    /**
+     * ApplicationPlaybackSource equivalent of the above.
+     */
+    virtual int getApplicationChannelCount() const {
+        return getTargetChannelCount();
+    }
+    
+    /**
+     * 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 sv_samplerate_t getSourceSampleRate() const;
+
+    /**
+     * ApplicationPlaybackSource equivalent of the above.
+     */
+    virtual int getApplicationSampleRate() const {
+        return int(round(getSourceSampleRate()));
+    }
+
+    /**
+     * 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.
+     */
+    virtual int getSourceSamples(int count, float **buffer);
+
+    /**
+     * Set the time stretcher factor (i.e. playback speed).
+     */
+    void setTimeStretch(double factor);
+
+    /**
+     * 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
+     * setAuditioningEffect (depending on real-time constraints).
+     *
+     * Pass a null pointer to remove the current auditioning plugin,
+     * if any.
+     */
+    void setAuditioningEffect(Auditionable *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();
+
+    std::string getClientName() const { return m_clientName; }
+
+signals:
+    void modelReplaced();
+
+    void playStatusChanged(bool isPlaying);
+
+    void sampleRateMismatch(sv_samplerate_t requested,
+                            sv_samplerate_t available,
+                            bool willResample);
+
+    void audioOverloadPluginDisabled();
+    void audioTimeStretchMultiChannelDisabled();
+
+    void activity(QString);
+
+public slots:
+    void audioProcessingOverload();
+
+protected slots:
+    void selectionChanged();
+    void playLoopModeChanged();
+    void playSelectionModeChanged();
+    void playParametersChanged(PlayParameters *);
+    void preferenceChanged(PropertyContainer::PropertyName);
+    void modelChangedWithin(sv_frame_t startFrame, sv_frame_t endFrame);
+
+protected:
+    ViewManagerBase                  *m_viewManager;
+    AudioGenerator                   *m_audioGenerator;
+    std::string                       m_clientName;
+
+    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;
+    sv_frame_t                        m_readBufferFill;
+    sv_frame_t                        m_writeBufferFill;
+    Scavenger<RingBufferVector>       m_bufferScavenger;
+    int                               m_sourceChannelCount;
+    sv_frame_t                        m_blockSize;
+    sv_samplerate_t                   m_sourceSampleRate;
+    sv_samplerate_t                   m_targetSampleRate;
+    sv_frame_t                        m_playLatency;
+    breakfastquay::SystemPlaybackTarget *m_target;
+    double                            m_lastRetrievalTimestamp;
+    sv_frame_t                        m_lastRetrievedBlockSize;
+    bool                              m_trustworthyTimestamps;
+    sv_frame_t                        m_lastCurrentFrame;
+    bool                              m_playing;
+    bool                              m_exiting;
+    sv_frame_t                        m_lastModelEndFrame;
+    int                               m_ringBufferSize;
+    float                             m_outputLeft;
+    float                             m_outputRight;
+    RealTimePluginInstance           *m_auditioningPlugin;
+    bool                              m_auditioningPluginBypassed;
+    Scavenger<RealTimePluginInstance> m_pluginScavenger;
+    sv_frame_t                        m_playStartFrame;
+    bool                              m_playStartFramePassed;
+    RealTime                          m_playStartedAt;
+
+    RingBuffer<float> *getWriteRingBuffer(int c) {
+	if (m_writeBuffers && c < (int)m_writeBuffers->size()) {
+	    return (*m_writeBuffers)[c];
+	} else {
+	    return 0;
+	}
+    }
+
+    RingBuffer<float> *getReadRingBuffer(int c) {
+	RingBufferVector *rb = m_readBuffers;
+	if (rb && c < (int)rb->size()) {
+	    return (*rb)[c];
+	} else {
+	    return 0;
+	}
+    }
+
+    void clearRingBuffers(bool haveLock = false, int count = 0);
+    void unifyRingBuffers();
+
+    RubberBand::RubberBandStretcher *m_timeStretcher;
+    RubberBand::RubberBandStretcher *m_monoStretcher;
+    double m_stretchRatio;
+    bool m_stretchMono;
+    
+    int m_stretcherInputCount;
+    float **m_stretcherInputs;
+    sv_frame_t *m_stretcherInputSizes;
+
+    // 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).
+    sv_frame_t mixModels(sv_frame_t &frame, sv_frame_t count, float **buffers);
+
+    // Called from getSourceSamples.
+    void applyAuditioningEffect(sv_frame_t count, float **buffers);
+
+    // Ranges of current selections, if play selection is active
+    std::vector<RealTime> m_rangeStarts;
+    std::vector<RealTime> m_rangeDurations;
+    void rebuildRangeLists();
+
+    sv_frame_t getCurrentFrame(RealTime outputLatency);
+
+    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;
+    int m_resampleQuality;
+    void initialiseConverter();
+};
+
+#endif
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/AudioGenerator.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,710 @@
+/* -*- 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/FlexiNoteModel.h"
+#include "data/model/DenseTimeValueModel.h"
+#include "data/model/SparseTimeValueModel.h"
+#include "data/model/SparseOneDimensionalModel.h"
+#include "data/model/NoteData.h"
+
+#include "ClipMixer.h"
+#include "ContinuousSynth.h"
+
+#include <iostream>
+#include <cmath>
+
+#include <QDir>
+#include <QFile>
+
+const sv_frame_t
+AudioGenerator::m_processingBlockSize = 1024;
+
+QString
+AudioGenerator::m_sampleDir = "";
+
+//#define DEBUG_AUDIO_GENERATOR 1
+
+AudioGenerator::AudioGenerator() :
+    m_sourceSampleRate(0),
+    m_targetChannelCount(1),
+    m_waveType(0),
+    m_soloing(false),
+    m_channelBuffer(0),
+    m_channelBufSiz(0),
+    m_channelBufCount(0)
+{
+    initialiseSampleDir();
+
+    connect(PlayParameterRepository::getInstance(),
+            SIGNAL(playClipIdChanged(const Playable *, QString)),
+            this,
+            SLOT(playClipIdChanged(const Playable *, QString)));
+}
+
+AudioGenerator::~AudioGenerator()
+{
+#ifdef DEBUG_AUDIO_GENERATOR
+    SVDEBUG << "AudioGenerator::~AudioGenerator" << endl;
+#endif
+}
+
+void
+AudioGenerator::initialiseSampleDir()
+{
+    if (m_sampleDir != "") return;
+
+    try {
+        m_sampleDir = TempDirectory::getInstance()->getSubDirectoryPath("samples");
+    } catch (DirectoryCreationFailed f) {
+        cerr << "WARNING: AudioGenerator::initialiseSampleDir:"
+                  << " Failed to create temporary sample directory"
+                  << 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));
+        QString target = QDir(m_sampleDir).filePath(fileName);
+
+        if (!file.copy(target)) {
+            cerr << "WARNING: AudioGenerator::getSampleDir: "
+                      << "Unable to copy " << fileName
+                      << " into temporary directory \""
+                      << m_sampleDir << "\"" << endl;
+        } else {
+            QFile tf(target);
+            tf.setPermissions(tf.permissions() |
+                              QFile::WriteOwner |
+                              QFile::WriteUser);
+        }
+    }
+}
+
+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;
+	}
+    }
+
+    const Playable *playable = model;
+    if (!playable || !playable->canPlay()) return 0;
+
+    PlayParameters *parameters =
+	PlayParameterRepository::getInstance()->getPlayParameters(playable);
+
+    bool willPlay = !parameters->isPlayMuted();
+    
+    if (usesClipMixer(model)) {
+        ClipMixer *mixer = makeClipMixerFor(model);
+        if (mixer) {
+            QMutexLocker locker(&m_mutex);
+            m_clipMixerMap[model] = mixer;
+            return willPlay;
+        }
+    }
+
+    if (usesContinuousSynth(model)) {
+        ContinuousSynth *synth = makeSynthFor(model);
+        if (synth) {
+            QMutexLocker locker(&m_mutex);
+            m_continuousSynthMap[model] = synth;
+            return willPlay;
+        }
+    }
+
+    return false;
+}
+
+void
+AudioGenerator::playClipIdChanged(const Playable *playable, QString)
+{
+    const Model *model = dynamic_cast<const Model *>(playable);
+    if (!model) {
+        cerr << "WARNING: AudioGenerator::playClipIdChanged: playable "
+                  << playable << " is not a supported model type"
+                  << endl;
+        return;
+    }
+
+    if (m_clipMixerMap.find(model) == m_clipMixerMap.end()) return;
+
+    ClipMixer *mixer = makeClipMixerFor(model);
+    if (mixer) {
+        QMutexLocker locker(&m_mutex);
+        m_clipMixerMap[model] = mixer;
+    }
+}
+
+bool
+AudioGenerator::usesClipMixer(const Model *model)
+{
+    bool clip = 
+        (qobject_cast<const SparseOneDimensionalModel *>(model) ||
+         qobject_cast<const NoteModel *>(model) ||
+         qobject_cast<const FlexiNoteModel *>(model));
+    return clip;
+}
+
+bool
+AudioGenerator::wantsQuieterClips(const Model *model)
+{
+    // basically, anything that usually has sustain (like notes) or
+    // often has multiple sounds at once (like notes) wants to use a
+    // quieter level than simple click tracks
+    bool does = 
+        (qobject_cast<const NoteModel *>(model) ||
+         qobject_cast<const FlexiNoteModel *>(model));
+    return does;
+}
+
+bool
+AudioGenerator::usesContinuousSynth(const Model *model)
+{
+    bool cont = 
+        (qobject_cast<const SparseTimeValueModel *>(model));
+    return cont;
+}
+
+ClipMixer *
+AudioGenerator::makeClipMixerFor(const Model *model)
+{
+    QString clipId;
+
+    const Playable *playable = model;
+    if (!playable || !playable->canPlay()) return 0;
+
+    PlayParameters *parameters =
+	PlayParameterRepository::getInstance()->getPlayParameters(playable);
+    if (parameters) {
+        clipId = parameters->getPlayClipId();
+    }
+
+#ifdef DEBUG_AUDIO_GENERATOR
+    std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): sample id = " << clipId << std::endl;
+#endif
+
+    if (clipId == "") {
+        SVDEBUG << "AudioGenerator::makeClipMixerFor(" << model << "): no sample, skipping" << endl;
+        return 0;
+    }
+
+    ClipMixer *mixer = new ClipMixer(m_targetChannelCount,
+                                     m_sourceSampleRate,
+                                     m_processingBlockSize);
+
+    double clipF0 = Pitch::getFrequencyForPitch(60, 0, 440.0); // required
+
+    QString clipPath = QString("%1/%2.wav").arg(m_sampleDir).arg(clipId);
+
+    double level = wantsQuieterClips(model) ? 0.5 : 1.0;
+    if (!mixer->loadClipData(clipPath, clipF0, level)) {
+        delete mixer;
+        return 0;
+    }
+
+#ifdef DEBUG_AUDIO_GENERATOR
+    std::cerr << "AudioGenerator::makeClipMixerFor(" << model << "): loaded clip " << clipId << std::endl;
+#endif
+
+    return mixer;
+}
+
+ContinuousSynth *
+AudioGenerator::makeSynthFor(const Model *model)
+{
+    const Playable *playable = model;
+    if (!playable || !playable->canPlay()) return 0;
+
+    ContinuousSynth *synth = new ContinuousSynth(m_targetChannelCount,
+                                                 m_sourceSampleRate,
+                                                 m_processingBlockSize,
+                                                 m_waveType);
+
+#ifdef DEBUG_AUDIO_GENERATOR
+    std::cerr << "AudioGenerator::makeSynthFor(" << model << "): created synth" << std::endl;
+#endif
+
+    return synth;
+}
+
+void
+AudioGenerator::removeModel(Model *model)
+{
+    SparseOneDimensionalModel *sodm =
+	dynamic_cast<SparseOneDimensionalModel *>(model);
+    if (!sodm) return; // nothing to do
+
+    QMutexLocker locker(&m_mutex);
+
+    if (m_clipMixerMap.find(sodm) == m_clipMixerMap.end()) return;
+
+    ClipMixer *mixer = m_clipMixerMap[sodm];
+    m_clipMixerMap.erase(sodm);
+    delete mixer;
+}
+
+void
+AudioGenerator::clearModels()
+{
+    QMutexLocker locker(&m_mutex);
+
+    while (!m_clipMixerMap.empty()) {
+        ClipMixer *mixer = m_clipMixerMap.begin()->second;
+	m_clipMixerMap.erase(m_clipMixerMap.begin());
+	delete mixer;
+    }
+}    
+
+void
+AudioGenerator::reset()
+{
+    QMutexLocker locker(&m_mutex);
+
+#ifdef DEBUG_AUDIO_GENERATOR
+    cerr << "AudioGenerator::reset()" << endl;
+#endif
+
+    for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
+	if (i->second) {
+	    i->second->reset();
+	}
+    }
+
+    m_noteOffs.clear();
+}
+
+void
+AudioGenerator::setTargetChannelCount(int targetChannelCount)
+{
+    if (m_targetChannelCount == targetChannelCount) return;
+
+//    SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << endl;
+
+    QMutexLocker locker(&m_mutex);
+    m_targetChannelCount = targetChannelCount;
+
+    for (ClipMixerMap::iterator i = m_clipMixerMap.begin(); i != m_clipMixerMap.end(); ++i) {
+	if (i->second) i->second->setChannelCount(targetChannelCount);
+    }
+}
+
+sv_frame_t
+AudioGenerator::getBlockSize() const
+{
+    return m_processingBlockSize;
+}
+
+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;
+}
+
+sv_frame_t
+AudioGenerator::mixModel(Model *model, sv_frame_t startFrame, sv_frame_t frameCount,
+			 float **buffer, sv_frame_t fadeIn, sv_frame_t fadeOut)
+{
+    if (m_sourceSampleRate == 0) {
+	cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << endl;
+	return frameCount;
+    }
+
+    QMutexLocker locker(&m_mutex);
+
+    Playable *playable = model;
+    if (!playable || !playable->canPlay()) return frameCount;
+
+    PlayParameters *parameters =
+	PlayParameterRepository::getInstance()->getPlayParameters(playable);
+    if (!parameters) return frameCount;
+
+    bool playing = !parameters->isPlayMuted();
+    if (!playing) {
+#ifdef DEBUG_AUDIO_GENERATOR
+        cout << "AudioGenerator::mixModel(" << model << "): muted" << endl;
+#endif
+        return frameCount;
+    }
+
+    if (m_soloing) {
+        if (m_soloModelSet.find(model) == m_soloModelSet.end()) {
+#ifdef DEBUG_AUDIO_GENERATOR
+            cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << 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);
+    }
+
+    if (usesClipMixer(model)) {
+        return mixClipModel(model, startFrame, frameCount,
+                            buffer, gain, pan);
+    }
+
+    if (usesContinuousSynth(model)) {
+        return mixContinuousSynthModel(model, startFrame, frameCount,
+                                       buffer, gain, pan);
+    }
+
+    std::cerr << "AudioGenerator::mixModel: WARNING: Model " << model << " of type " << model->getTypeName() << " is marked as playable, but I have no mechanism to play it" << std::endl;
+
+    return frameCount;
+}
+
+sv_frame_t
+AudioGenerator::mixDenseTimeValueModel(DenseTimeValueModel *dtvm,
+				       sv_frame_t startFrame, sv_frame_t frames,
+				       float **buffer, float gain, float pan,
+				       sv_frame_t fadeIn, sv_frame_t fadeOut)
+{
+    sv_frame_t maxFrames = frames + std::max(fadeIn, fadeOut);
+
+    int modelChannels = dtvm->getChannelCount();
+
+    if (m_channelBufSiz < maxFrames || m_channelBufCount < modelChannels) {
+
+        for (int c = 0; c < m_channelBufCount; ++c) {
+            delete[] m_channelBuffer[c];
+        }
+
+	delete[] m_channelBuffer;
+        m_channelBuffer = new float *[modelChannels];
+
+        for (int c = 0; c < modelChannels; ++c) {
+            m_channelBuffer[c] = new float[maxFrames];
+        }
+
+        m_channelBufCount = modelChannels;
+	m_channelBufSiz = maxFrames;
+    }
+
+    sv_frame_t got = 0;
+
+    if (startFrame >= fadeIn/2) {
+
+        auto data = dtvm->getMultiChannelData(0, modelChannels - 1,
+                                              startFrame - fadeIn/2,
+                                              frames + fadeOut/2 + fadeIn/2);
+
+        for (int c = 0; c < modelChannels; ++c) {
+            copy(data[c].begin(), data[c].end(), m_channelBuffer[c]);
+        }
+
+        got = data[0].size();
+
+    } else {
+        sv_frame_t missing = fadeIn/2 - startFrame;
+
+        if (missing > 0) {
+            cerr << "note: channelBufSiz = " << m_channelBufSiz
+                 << ", frames + fadeOut/2 = " << frames + fadeOut/2 
+                 << ", startFrame = " << startFrame 
+                 << ", missing = " << missing << endl;
+        }
+
+        auto data = dtvm->getMultiChannelData(0, modelChannels - 1,
+                                              startFrame,
+                                              frames + fadeOut/2);
+        for (int c = 0; c < modelChannels; ++c) {
+            copy(data[c].begin(), data[c].end(), m_channelBuffer[c] + missing);
+        }
+
+        got = data[0].size() + missing;
+    }	    
+
+    for (int c = 0; c < m_targetChannelCount; ++c) {
+
+	int sourceChannel = (c % modelChannels);
+
+//	SVDEBUG << "mixing channel " << c << " from source channel " << sourceChannel << endl;
+
+	float channelGain = gain;
+	if (pan != 0.0) {
+	    if (c == 0) {
+		if (pan > 0.0) channelGain *= 1.0f - pan;
+	    } else {
+		if (pan < 0.0) channelGain *= pan + 1.0f;
+	    }
+	}
+
+	for (sv_frame_t i = 0; i < fadeIn/2; ++i) {
+	    float *back = buffer[c];
+	    back -= fadeIn/2;
+	    back[i] +=
+                (channelGain * m_channelBuffer[sourceChannel][i] * float(i))
+                / float(fadeIn);
+	}
+
+	for (sv_frame_t i = 0; i < frames + fadeOut/2; ++i) {
+	    float mult = channelGain;
+	    if (i < fadeIn/2) {
+		mult = (mult * float(i)) / float(fadeIn);
+	    }
+	    if (i > frames - fadeOut/2) {
+		mult = (mult * float((frames + fadeOut/2) - i)) / float(fadeOut);
+	    }
+            float val = m_channelBuffer[sourceChannel][i];
+            if (i >= got) val = 0.f;
+	    buffer[c][i] += mult * val;
+	}
+    }
+
+    return got;
+}
+  
+sv_frame_t
+AudioGenerator::mixClipModel(Model *model,
+                             sv_frame_t startFrame, sv_frame_t frames,
+                             float **buffer, float gain, float pan)
+{
+    ClipMixer *clipMixer = m_clipMixerMap[model];
+    if (!clipMixer) return 0;
+
+    int blocks = int(frames / m_processingBlockSize);
+    
+    //!!! todo: the below -- it matters
+
+    //!!! 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
+
+    sv_frame_t got = blocks * m_processingBlockSize;
+
+#ifdef DEBUG_AUDIO_GENERATOR
+    cout << "mixModel [clip]: start " << startFrame << ", frames " << frames
+         << ", blocks " << blocks << ", have " << m_noteOffs.size()
+         << " note-offs" << endl;
+#endif
+
+    ClipMixer::NoteStart on;
+    ClipMixer::NoteEnd off;
+
+    NoteOffSet &noteOffs = m_noteOffs[model];
+
+    float **bufferIndexes = new float *[m_targetChannelCount];
+
+    for (int i = 0; i < blocks; ++i) {
+
+	sv_frame_t reqStart = startFrame + i * m_processingBlockSize;
+
+        NoteList notes;
+        NoteExportable *exportable = dynamic_cast<NoteExportable *>(model);
+        if (exportable) {
+            notes = exportable->getNotesWithin(reqStart,
+                                               reqStart + m_processingBlockSize);
+        }
+
+        std::vector<ClipMixer::NoteStart> starts;
+        std::vector<ClipMixer::NoteEnd> ends;
+
+	for (NoteList::const_iterator ni = notes.begin();
+             ni != notes.end(); ++ni) {
+
+	    sv_frame_t noteFrame = ni->start;
+
+	    if (noteFrame < reqStart ||
+		noteFrame >= reqStart + m_processingBlockSize) continue;
+
+	    while (noteOffs.begin() != noteOffs.end() &&
+		   noteOffs.begin()->frame <= noteFrame) {
+
+                sv_frame_t eventFrame = noteOffs.begin()->frame;
+                if (eventFrame < reqStart) eventFrame = reqStart;
+
+                off.frameOffset = eventFrame - reqStart;
+                off.frequency = noteOffs.begin()->frequency;
+
+#ifdef DEBUG_AUDIO_GENERATOR
+		cerr << "mixModel [clip]: adding note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
+#endif
+
+                ends.push_back(off);
+		noteOffs.erase(noteOffs.begin());
+	    }
+
+            on.frameOffset = noteFrame - reqStart;
+            on.frequency = ni->getFrequency();
+            on.level = float(ni->velocity) / 127.0f;
+            on.pan = pan;
+
+#ifdef DEBUG_AUDIO_GENERATOR
+	    cout << "mixModel [clip]: adding note at frame " << noteFrame << ", frame offset " << on.frameOffset << " frequency " << on.frequency << ", level " << on.level << endl;
+#endif
+	    
+            starts.push_back(on);
+	    noteOffs.insert
+                (NoteOff(on.frequency, noteFrame + ni->duration));
+	}
+
+	while (noteOffs.begin() != noteOffs.end() &&
+	       noteOffs.begin()->frame <= reqStart + m_processingBlockSize) {
+
+            sv_frame_t eventFrame = noteOffs.begin()->frame;
+            if (eventFrame < reqStart) eventFrame = reqStart;
+
+            off.frameOffset = eventFrame - reqStart;
+            off.frequency = noteOffs.begin()->frequency;
+
+#ifdef DEBUG_AUDIO_GENERATOR
+            cerr << "mixModel [clip]: adding leftover note-off at frame " << eventFrame << " frame offset " << off.frameOffset << " frequency " << off.frequency << endl;
+#endif
+
+            ends.push_back(off);
+            noteOffs.erase(noteOffs.begin());
+	}
+
+	for (int c = 0; c < m_targetChannelCount; ++c) {
+            bufferIndexes[c] = buffer[c] + i * m_processingBlockSize;
+        }
+
+        clipMixer->mix(bufferIndexes, gain, starts, ends);
+    }
+
+    delete[] bufferIndexes;
+
+    return got;
+}
+
+sv_frame_t
+AudioGenerator::mixContinuousSynthModel(Model *model,
+                                        sv_frame_t startFrame,
+                                        sv_frame_t frames,
+                                        float **buffer,
+                                        float gain, 
+                                        float pan)
+{
+    ContinuousSynth *synth = m_continuousSynthMap[model];
+    if (!synth) return 0;
+
+    // only type we support here at the moment
+    SparseTimeValueModel *stvm = qobject_cast<SparseTimeValueModel *>(model);
+    if (stvm->getScaleUnits() != "Hz") return 0;
+
+    int blocks = int(frames / m_processingBlockSize);
+
+    //!!! todo: see comment in mixClipModel
+
+    sv_frame_t got = blocks * m_processingBlockSize;
+
+#ifdef DEBUG_AUDIO_GENERATOR
+    cout << "mixModel [synth]: frames " << frames
+	      << ", blocks " << blocks << endl;
+#endif
+    
+    float **bufferIndexes = new float *[m_targetChannelCount];
+
+    for (int i = 0; i < blocks; ++i) {
+
+	sv_frame_t reqStart = startFrame + i * m_processingBlockSize;
+
+	for (int c = 0; c < m_targetChannelCount; ++c) {
+            bufferIndexes[c] = buffer[c] + i * m_processingBlockSize;
+        }
+
+        SparseTimeValueModel::PointList points = 
+            stvm->getPoints(reqStart, reqStart + m_processingBlockSize);
+
+        // by default, repeat last frequency
+        float f0 = 0.f;
+
+        // go straight to the last freq that is genuinely in this range
+        for (SparseTimeValueModel::PointList::const_iterator itr = points.end();
+             itr != points.begin(); ) {
+            --itr;
+            if (itr->frame >= reqStart &&
+                itr->frame < reqStart + m_processingBlockSize) {
+                f0 = itr->value;
+                break;
+            }
+        }
+
+        // if we found no such frequency and the next point is further
+        // away than twice the model resolution, go silent (same
+        // criterion TimeValueLayer uses for ending a discrete curve
+        // segment)
+        if (f0 == 0.f) {
+            SparseTimeValueModel::PointList nextPoints = 
+                stvm->getNextPoints(reqStart + m_processingBlockSize);
+            if (nextPoints.empty() ||
+                nextPoints.begin()->frame > reqStart + 2 * stvm->getResolution()) {
+                f0 = -1.f;
+            }
+        }
+
+//        cerr << "f0 = " << f0 << endl;
+
+        synth->mix(bufferIndexes,
+                   gain,
+                   pan,
+                   f0);
+    }
+
+    delete[] bufferIndexes;
+
+    return got;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/AudioGenerator.h	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,168 @@
+/* -*- 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 FlexiNoteModel;
+class DenseTimeValueModel;
+class SparseOneDimensionalModel;
+class Playable;
+class ClipMixer;
+class ContinuousSynth;
+
+#include <QObject>
+#include <QMutex>
+
+#include <set>
+#include <map>
+#include <vector>
+
+#include "base/BaseTypes.h"
+
+class AudioGenerator : public QObject
+{
+    Q_OBJECT
+
+public:
+    AudioGenerator();
+    virtual ~AudioGenerator();
+
+    /**
+     * Add a data model to be played from and initialise any necessary
+     * audio generation code.  Returns true if the model will be
+     * played.  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 buffers 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(int channelCount);
+
+    /**
+     * Return the internal processing block size.  The frameCount
+     * argument to all mixModel calls must be a multiple of this
+     * value.
+     */
+    virtual sv_frame_t getBlockSize() const;
+
+    /**
+     * Mix a single model into an output buffer.
+     */
+    virtual sv_frame_t mixModel(Model *model, sv_frame_t startFrame, sv_frame_t frameCount,
+			    float **buffer, sv_frame_t fadeIn = 0, sv_frame_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 playClipIdChanged(const Playable *, QString);
+
+protected:
+    sv_samplerate_t m_sourceSampleRate;
+    int m_targetChannelCount;
+    int m_waveType;
+
+    bool m_soloing;
+    std::set<Model *> m_soloModelSet;
+
+    struct NoteOff {
+
+        NoteOff(float _freq, sv_frame_t _frame) : frequency(_freq), frame(_frame) { }
+
+        float frequency;
+	sv_frame_t frame;
+
+	struct Comparator {
+	    bool operator()(const NoteOff &n1, const NoteOff &n2) const {
+		return n1.frame < n2.frame;
+	    }
+	};
+    };
+
+
+    typedef std::map<const Model *, ClipMixer *> ClipMixerMap;
+
+    typedef std::multiset<NoteOff, NoteOff::Comparator> NoteOffSet;
+    typedef std::map<const Model *, NoteOffSet> NoteOffMap;
+
+    typedef std::map<const Model *, ContinuousSynth *> ContinuousSynthMap;
+
+    QMutex m_mutex;
+
+    ClipMixerMap m_clipMixerMap;
+    NoteOffMap m_noteOffs;
+    static QString m_sampleDir;
+
+    ContinuousSynthMap m_continuousSynthMap;
+
+    bool usesClipMixer(const Model *);
+    bool wantsQuieterClips(const Model *);
+    bool usesContinuousSynth(const Model *);
+
+    ClipMixer *makeClipMixerFor(const Model *model);
+    ContinuousSynth *makeSynthFor(const Model *model);
+
+    static void initialiseSampleDir();
+
+    virtual sv_frame_t mixDenseTimeValueModel
+    (DenseTimeValueModel *model, sv_frame_t startFrame, sv_frame_t frameCount,
+     float **buffer, float gain, float pan, sv_frame_t fadeIn, sv_frame_t fadeOut);
+
+    virtual sv_frame_t mixClipModel
+    (Model *model, sv_frame_t startFrame, sv_frame_t frameCount,
+     float **buffer, float gain, float pan);
+
+    virtual sv_frame_t mixContinuousSynthModel
+    (Model *model, sv_frame_t startFrame, sv_frame_t frameCount,
+     float **buffer, float gain, float pan);
+    
+    static const sv_frame_t m_processingBlockSize;
+
+    float **m_channelBuffer;
+    sv_frame_t m_channelBufSiz;
+    int m_channelBufCount;
+};
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/AudioRecordTarget.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,193 @@
+/* -*- 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.
+*/
+
+#include "AudioRecordTarget.h"
+
+#include "base/ViewManagerBase.h"
+#include "base/TempDirectory.h"
+
+#include "data/model/WritableWaveFileModel.h"
+
+#include <QDir>
+
+AudioRecordTarget::AudioRecordTarget(ViewManagerBase *manager,
+				     QString clientName) :
+    m_viewManager(manager),
+    m_clientName(clientName.toUtf8().data()),
+    m_recording(false),
+    m_recordSampleRate(44100),
+    m_frameCount(0),
+    m_model(0)
+{
+}
+
+AudioRecordTarget::~AudioRecordTarget()
+{
+    QMutexLocker locker(&m_mutex);
+}
+
+void
+AudioRecordTarget::setSystemRecordBlockSize(int)
+{
+}
+
+void
+AudioRecordTarget::setSystemRecordSampleRate(int n)
+{
+    m_recordSampleRate = n;
+}
+
+void
+AudioRecordTarget::setSystemRecordLatency(int)
+{
+}
+
+void
+AudioRecordTarget::putSamples(int nframes, float **samples)
+{
+    bool secChanged = false;
+    sv_frame_t frameToEmit = 0;
+
+    {
+        QMutexLocker locker(&m_mutex); //!!! bad here
+        if (!m_recording) return;
+
+        m_model->addSamples(samples, nframes);
+
+        sv_frame_t priorFrameCount = m_frameCount;
+        m_frameCount += nframes;
+
+        RealTime priorRT = RealTime::frame2RealTime
+            (priorFrameCount, m_recordSampleRate);
+        RealTime postRT = RealTime::frame2RealTime
+            (m_frameCount, m_recordSampleRate);
+
+        secChanged = (postRT.sec > priorRT.sec);
+        if (secChanged) frameToEmit = m_frameCount;
+    }
+
+    if (secChanged) {
+        emit recordDurationChanged(frameToEmit, m_recordSampleRate);
+    }
+}
+
+void
+AudioRecordTarget::setInputLevels(float, float)
+{
+}
+
+void
+AudioRecordTarget::modelAboutToBeDeleted()
+{
+    QMutexLocker locker(&m_mutex);
+    if (sender() == m_model) {
+        m_model = 0;
+        m_recording = false;
+    }
+}
+
+QString
+AudioRecordTarget::getRecordContainerFolder()
+{
+    QDir parent(TempDirectory::getInstance()->getContainingPath());
+    QString subdirname("recorded");
+
+    if (!parent.mkpath(subdirname)) {
+        cerr << "ERROR: AudioRecordTarget::getRecordContainerFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl;
+        return "";
+    } else {
+        return parent.filePath(subdirname);
+    }
+}
+
+QString
+AudioRecordTarget::getRecordFolder()
+{
+    QDir parent(getRecordContainerFolder());
+    QDateTime now = QDateTime::currentDateTime();
+    QString subdirname = QString("%1").arg(now.toString("yyyyMMdd"));
+
+    if (!parent.mkpath(subdirname)) {
+        cerr << "ERROR: AudioRecordTarget::getRecordFolder: Failed to create recorded dir in \"" << parent.canonicalPath() << "\"" << endl;
+        return "";
+    } else {
+        return parent.filePath(subdirname);
+    }
+}
+
+WritableWaveFileModel *
+AudioRecordTarget::startRecording()
+{
+    {
+    QMutexLocker locker(&m_mutex);
+    
+    if (m_recording) {
+        cerr << "WARNING: AudioRecordTarget::startRecording: We are already recording" << endl;
+        return 0;
+    }
+
+    m_model = 0;
+    m_frameCount = 0;
+
+    QString folder = getRecordFolder();
+    if (folder == "") return 0;
+    QDir recordedDir(folder);
+
+    QDateTime now = QDateTime::currentDateTime();
+
+    // Don't use QDateTime::toString(Qt::ISODate) as the ":" character
+    // isn't permitted in filenames on Windows
+    QString filename = QString("recorded-%1.wav")
+        .arg(now.toString("yyyyMMdd-HHmmss-zzz"));
+
+    m_audioFileName = recordedDir.filePath(filename);
+
+    m_model = new WritableWaveFileModel(m_recordSampleRate, 2, m_audioFileName);
+
+    if (!m_model->isOK()) {
+        cerr << "ERROR: AudioRecordTarget::startRecording: Recording failed"
+             << endl;
+        //!!! and throw?
+        delete m_model;
+        m_model = 0;
+        return 0;
+    }
+
+    m_recording = true;
+    }
+
+    emit recordStatusChanged(true);
+    return m_model;
+}
+
+void
+AudioRecordTarget::stopRecording()
+{
+    {
+    QMutexLocker locker(&m_mutex);
+    if (!m_recording) {
+        cerr << "WARNING: AudioRecordTarget::startRecording: Not recording" << endl;
+        return;
+    }
+
+    m_model->writeComplete();
+    m_model = 0;
+    m_recording = false;
+    }
+
+    emit recordStatusChanged(false);
+    emit recordCompleted();
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/AudioRecordTarget.h	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,80 @@
+/* -*- 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.
+*/
+
+#ifndef AUDIO_RECORD_TARGET_H
+#define AUDIO_RECORD_TARGET_H
+
+#include <bqaudioio/ApplicationRecordTarget.h>
+
+#include <string>
+
+#include <QObject>
+#include <QMutex>
+
+#include "base/BaseTypes.h"
+
+class ViewManagerBase;
+class WritableWaveFileModel;
+
+class AudioRecordTarget : public QObject,
+			  public breakfastquay::ApplicationRecordTarget
+{
+    Q_OBJECT
+
+public:
+    AudioRecordTarget(ViewManagerBase *, QString clientName);
+    virtual ~AudioRecordTarget();
+
+    virtual std::string getClientName() const { return m_clientName; }
+    
+    virtual int getApplicationSampleRate() const { return 0; } // don't care
+    virtual int getApplicationChannelCount() const { return 2; }
+
+    virtual void setSystemRecordBlockSize(int);
+    virtual void setSystemRecordSampleRate(int);
+    virtual void setSystemRecordLatency(int);
+
+    virtual void putSamples(int nframes, float **samples);
+    
+    virtual void setInputLevels(float peakLeft, float peakRight);
+
+    virtual void audioProcessingOverload() { }
+
+    QString getRecordContainerFolder();
+    QString getRecordFolder();
+    
+    bool isRecording() const { return m_recording; }
+    WritableWaveFileModel *startRecording(); // caller takes ownership
+    void stopRecording();
+
+signals:
+    void recordStatusChanged(bool recording);
+    void recordDurationChanged(sv_frame_t, sv_samplerate_t); // emitted occasionally
+    void recordCompleted();
+
+protected slots:
+    void modelAboutToBeDeleted();
+    
+private:
+    ViewManagerBase *m_viewManager;
+    std::string m_clientName;
+    bool m_recording;
+    sv_samplerate_t m_recordSampleRate;
+    sv_frame_t m_frameCount;
+    QString m_audioFileName;
+    WritableWaveFileModel *m_model;
+    QMutex m_mutex;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/ClipMixer.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,248 @@
+/* -*- 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, 2006-2014 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 "ClipMixer.h"
+
+#include <sndfile.h>
+#include <cmath>
+
+#include "base/Debug.h"
+
+//#define DEBUG_CLIP_MIXER 1
+
+ClipMixer::ClipMixer(int channels, sv_samplerate_t sampleRate, sv_frame_t blockSize) :
+    m_channels(channels),
+    m_sampleRate(sampleRate),
+    m_blockSize(blockSize),
+    m_clipData(0),
+    m_clipLength(0),
+    m_clipF0(0),
+    m_clipRate(0)
+{
+}
+
+ClipMixer::~ClipMixer()
+{
+    if (m_clipData) free(m_clipData);
+}
+
+void
+ClipMixer::setChannelCount(int channels)
+{
+    m_channels = channels;
+}
+
+bool
+ClipMixer::loadClipData(QString path, double f0, double level)
+{
+    if (m_clipData) {
+        cerr << "ClipMixer::loadClipData: Already have clip loaded" << endl;
+        return false;
+    }
+
+    SF_INFO info;
+    SNDFILE *file;
+    float *tmpFrames;
+    sv_frame_t i;
+
+    info.format = 0;
+    file = sf_open(path.toLocal8Bit().data(), SFM_READ, &info);
+    if (!file) {
+	cerr << "ClipMixer::loadClipData: Failed to open file path \""
+             << path << "\": " << sf_strerror(file) << endl;
+	return false;
+    }
+
+    tmpFrames = (float *)malloc(info.frames * info.channels * sizeof(float));
+    if (!tmpFrames) {
+        cerr << "ClipMixer::loadClipData: malloc(" << info.frames * info.channels * sizeof(float) << ") failed" << endl;
+        return false;
+    }
+
+    sf_readf_float(file, tmpFrames, info.frames);
+    sf_close(file);
+
+    m_clipData = (float *)malloc(info.frames * sizeof(float));
+    if (!m_clipData) {
+        cerr << "ClipMixer::loadClipData: malloc(" << info.frames * sizeof(float) << ") failed" << endl;
+	free(tmpFrames);
+	return false;
+    }
+
+    for (i = 0; i < info.frames; ++i) {
+	int j;
+	m_clipData[i] = 0.0f;
+	for (j = 0; j < info.channels; ++j) {
+	    m_clipData[i] += tmpFrames[i * info.channels + j] * float(level);
+	}
+    }
+
+    free(tmpFrames);
+
+    m_clipLength = info.frames;
+    m_clipF0 = f0;
+    m_clipRate = info.samplerate;
+
+    return true;
+}
+
+void
+ClipMixer::reset()
+{
+    m_playing.clear();
+}
+
+double
+ClipMixer::getResampleRatioFor(double frequency)
+{
+    if (!m_clipData || !m_clipRate) return 1.0;
+    double pitchRatio = m_clipF0 / frequency;
+    double resampleRatio = m_sampleRate / m_clipRate;
+    return pitchRatio * resampleRatio;
+}
+
+sv_frame_t
+ClipMixer::getResampledClipDuration(double frequency)
+{
+    return sv_frame_t(ceil(double(m_clipLength) * getResampleRatioFor(frequency)));
+}
+
+void
+ClipMixer::mix(float **toBuffers, 
+               float gain,
+               std::vector<NoteStart> newNotes, 
+               std::vector<NoteEnd> endingNotes)
+{
+    foreach (NoteStart note, newNotes) {
+        if (note.frequency > 20 && 
+            note.frequency < 5000) {
+            m_playing.push_back(note);
+        }
+    }
+
+    std::vector<NoteStart> remaining;
+
+    float *levels = new float[m_channels];
+
+#ifdef DEBUG_CLIP_MIXER
+    cerr << "ClipMixer::mix: have " << m_playing.size() << " playing note(s)"
+         << " and " << endingNotes.size() << " note(s) ending here"
+         << endl;
+#endif
+
+    foreach (NoteStart note, m_playing) {
+
+        for (int c = 0; c < m_channels; ++c) {
+            levels[c] = note.level * gain;
+        }
+        if (note.pan != 0.0 && m_channels == 2) {
+            levels[0] *= 1.0f - note.pan;
+            levels[1] *= note.pan + 1.0f;
+        }
+
+        sv_frame_t start = note.frameOffset;
+        sv_frame_t durationHere = m_blockSize;
+        if (start > 0) durationHere = m_blockSize - start;
+
+        bool ending = false;
+
+        foreach (NoteEnd end, endingNotes) {
+            if (end.frequency == note.frequency && 
+                end.frameOffset >= start &&
+                end.frameOffset <= m_blockSize) {
+                ending = true;
+                durationHere = end.frameOffset;
+                if (start > 0) durationHere = end.frameOffset - start;
+                break;
+            }
+        }
+
+        sv_frame_t clipDuration = getResampledClipDuration(note.frequency);
+        if (start + clipDuration > 0) {
+            if (start < 0 && start + clipDuration < durationHere) {
+                durationHere = start + clipDuration;
+            }
+            if (durationHere > 0) {
+                mixNote(toBuffers,
+                        levels,
+                        note.frequency,
+                        start < 0 ? -start : 0,
+                        start > 0 ?  start : 0,
+                        durationHere,
+                        ending);
+            }
+        }
+
+        if (!ending) {
+            NoteStart adjusted = note;
+            adjusted.frameOffset -= m_blockSize;
+            remaining.push_back(adjusted);
+        }
+    }
+
+    delete[] levels;
+
+    m_playing = remaining;
+}
+
+void
+ClipMixer::mixNote(float **toBuffers,
+                   float *levels,
+                   float frequency,
+                   sv_frame_t sourceOffset,
+                   sv_frame_t targetOffset,
+                   sv_frame_t sampleCount,
+                   bool isEnd)
+{
+    if (!m_clipData) return;
+
+    double ratio = getResampleRatioFor(frequency);
+    
+    double releaseTime = 0.01;
+    sv_frame_t releaseSampleCount = sv_frame_t(round(releaseTime * m_sampleRate));
+    if (releaseSampleCount > sampleCount) {
+        releaseSampleCount = sampleCount;
+    }
+    double releaseFraction = 1.0/double(releaseSampleCount);
+
+    for (sv_frame_t i = 0; i < sampleCount; ++i) {
+
+        sv_frame_t s = sourceOffset + i;
+
+        double os = double(s) / ratio;
+        sv_frame_t osi = sv_frame_t(floor(os));
+
+        //!!! just linear interpolation for now (same as SV's sample
+        //!!! player). a small sinc kernel would be better and
+        //!!! probably "good enough"
+        double value = 0.0;
+        if (osi < m_clipLength) {
+            value += m_clipData[osi];
+        }
+        if (osi + 1 < m_clipLength) {
+            value += (m_clipData[osi + 1] - m_clipData[osi]) * (os - double(osi));
+        }
+         
+        if (isEnd && i + releaseSampleCount > sampleCount) {
+            value *= releaseFraction * double(sampleCount - i); // linear ramp for release
+        }
+
+        for (int c = 0; c < m_channels; ++c) {
+            toBuffers[c][targetOffset + i] += float(levels[c] * value);
+        }
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/ClipMixer.h	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,94 @@
+/* -*- 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, 2006-2014 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 CLIP_MIXER_H
+#define CLIP_MIXER_H
+
+#include <QString>
+#include <vector>
+
+#include "base/BaseTypes.h"
+
+/**
+ * Mix in synthetic notes produced by resampling a prerecorded
+ * clip. (i.e. this is an implementation of a digital sampler in the
+ * musician's sense.) This can mix any number of notes of arbitrary
+ * frequency, so long as they all use the same sample clip.
+ */
+
+class ClipMixer
+{
+public:
+    ClipMixer(int channels, sv_samplerate_t sampleRate, sv_frame_t blockSize);
+    ~ClipMixer();
+
+    void setChannelCount(int channels);
+
+    /**
+     * Load a sample clip from a wav file. This can only happen once:
+     * construct a new ClipMixer if you want a different clip. The
+     * clip was recorded at a pitch with fundamental frequency clipF0,
+     * and should be scaled by level (in the range 0-1) when playing
+     * back.
+     */
+    bool loadClipData(QString clipFilePath, double clipF0, double level);
+
+    void reset(); // discarding any playing notes
+
+    struct NoteStart {
+	sv_frame_t frameOffset; // within current processing block
+	float frequency; // Hz
+	float level; // volume in range (0,1]
+	float pan; // range [-1,1]
+    };
+
+    struct NoteEnd {
+	sv_frame_t frameOffset; // in current processing block
+        float frequency; // matching note start
+    };
+
+    void mix(float **toBuffers, 
+             float gain,
+	     std::vector<NoteStart> newNotes, 
+	     std::vector<NoteEnd> endingNotes);
+
+private:
+    int m_channels;
+    sv_samplerate_t m_sampleRate;
+    sv_frame_t m_blockSize;
+
+    QString m_clipPath;
+
+    float *m_clipData;
+    sv_frame_t m_clipLength;
+    double m_clipF0;
+    sv_samplerate_t m_clipRate;
+
+    std::vector<NoteStart> m_playing;
+
+    double getResampleRatioFor(double frequency);
+    sv_frame_t getResampledClipDuration(double frequency);
+
+    void mixNote(float **toBuffers, 
+                 float *levels,
+                 float frequency,
+                 sv_frame_t sourceOffset, // within resampled note
+                 sv_frame_t targetOffset, // within target buffer
+                 sv_frame_t sampleCount,
+                 bool isEnd);
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/ContinuousSynth.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,149 @@
+/* -*- 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.
+*/
+
+#include "ContinuousSynth.h"
+
+#include "base/Debug.h"
+#include "system/System.h"
+
+#include <cmath>
+
+ContinuousSynth::ContinuousSynth(int channels, sv_samplerate_t sampleRate, sv_frame_t blockSize, int waveType) :
+    m_channels(channels),
+    m_sampleRate(sampleRate),
+    m_blockSize(blockSize),
+    m_prevF0(-1.0),
+    m_phase(0.0),
+    m_wavetype(waveType) // 0: 3 sinusoids, 1: 1 sinusoid, 2: sawtooth, 3: square
+{
+}
+
+ContinuousSynth::~ContinuousSynth()
+{
+}
+
+void
+ContinuousSynth::reset()
+{
+    m_phase = 0;
+}
+
+void
+ContinuousSynth::mix(float **toBuffers, float gain, float pan, float f0f)
+{
+    double f0(f0f);
+    if (f0 == 0.0) f0 = m_prevF0;
+
+    bool wasOn = (m_prevF0 > 0.0);
+    bool nowOn = (f0 > 0.0);
+
+    if (!nowOn && !wasOn) {
+        m_phase = 0;
+        return;
+    }
+
+    sv_frame_t fadeLength = 100;
+
+    float *levels = new float[m_channels];
+    
+    for (int c = 0; c < m_channels; ++c) {
+        levels[c] = gain * 0.5f; // scale gain otherwise too loud compared to source
+    }
+    if (pan != 0.0 && m_channels == 2) {
+        levels[0] *= 1.0f - pan;
+        levels[1] *= pan + 1.0f;
+    }
+
+//    cerr << "ContinuousSynth::mix: f0 = " << f0 << " (from " << m_prevF0 << "), phase = " << m_phase << endl;
+
+    for (sv_frame_t i = 0; i < m_blockSize; ++i) {
+
+        double fHere = (nowOn ? f0 : m_prevF0);
+
+        if (wasOn && nowOn && (f0 != m_prevF0) && (i < fadeLength)) {
+            // interpolate the frequency shift
+            fHere = m_prevF0 + ((f0 - m_prevF0) * double(i)) / double(fadeLength);
+        }
+
+        double phasor = (fHere * 2 * M_PI) / m_sampleRate;
+    
+        m_phase = m_phase + phasor;
+
+        int harmonics = int((m_sampleRate / 4) / fHere - 1);
+        if (harmonics < 1) harmonics = 1;
+
+        switch (m_wavetype) {
+        case 1:
+            harmonics = 1;
+            break;
+        case 2:
+            break;
+        case 3:
+            break;
+        default:
+            harmonics = 3;
+            break;
+        }
+
+        for (int h = 0; h < harmonics; ++h) {
+
+            double v = 0;
+            double hn = 0;
+            double hp = 0;
+
+            switch (m_wavetype) {
+            case 1: // single sinusoid
+                v = sin(m_phase);
+                break;
+            case 2: // sawtooth
+                if (h != 0) {
+                    hn = h + 1;
+                    hp = m_phase * hn;
+                    v = -(1.0 / M_PI) * sin(hp) / hn;
+                } else {
+                    v = 0.5;
+                }
+                break;
+            case 3: // square
+                hn = h*2 + 1;
+                hp = m_phase * hn;
+                v = sin(hp) / hn;
+                break;
+            default: // 3 sinusoids
+                hn = h + 1;
+                hp = m_phase * hn;
+                v = sin(hp) / hn;
+                break;
+            }
+
+            if (!wasOn && i < fadeLength) {
+                // fade in
+                v = v * (double(i) / double(fadeLength));
+            } else if (!nowOn) {
+                // fade out
+                if (i > fadeLength) v = 0;
+                else v = v * (1.0 - (double(i) / double(fadeLength)));
+            }
+
+            for (int c = 0; c < m_channels; ++c) {
+                toBuffers[c][i] += float(levels[c] * v);
+            }
+        }
+    }    
+
+    m_prevF0 = f0;
+
+    delete[] levels;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/ContinuousSynth.h	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,65 @@
+/* -*- 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.
+*/
+
+#ifndef CONTINUOUS_SYNTH_H
+#define CONTINUOUS_SYNTH_H
+
+#include "base/BaseTypes.h"
+
+/**
+ * Mix into a target buffer a signal synthesised so as to sound at a
+ * specific frequency. The frequency may change with each processing
+ * block, or may be switched on or off.
+ */
+
+class ContinuousSynth
+{
+public:
+    ContinuousSynth(int channels, sv_samplerate_t sampleRate, sv_frame_t blockSize, int waveType);
+    ~ContinuousSynth();
+    
+    void setChannelCount(int channels);
+
+    void reset();
+
+    /**
+     * Mix in a signal to be heard at the given fundamental
+     * frequency. Any oscillator state will be maintained between
+     * process calls so as to provide a continuous sound. The f0 value
+     * may vary between calls.
+     *
+     * Supply f0 equal to 0 if you want to maintain the f0 from the
+     * previous block (without having to remember what it was).
+     *
+     * Supply f0 less than 0 for silence. You should continue to call
+     * this even when the signal is silent if you want to ensure the
+     * sound switches on and off cleanly.
+     */
+    void mix(float **toBuffers,
+             float gain,
+             float pan,
+             float f0);
+
+private:
+    int m_channels;
+    sv_samplerate_t m_sampleRate;
+    sv_frame_t m_blockSize;
+
+    double m_prevF0;
+    double m_phase;
+
+    int m_wavetype;
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/PlaySpeedRangeMapper.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,101 @@
+/* -*- 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 maps a position in the range [0,120] on to a
+// play speed factor on a logarithmic scale in the range 0.125 ->
+// 8. This ensures that the desirable speed factors 0.25, 0.5, 1, 2,
+// and 4 are all mapped to exact positions (respectively 20, 40, 60,
+// 80, 100).
+
+// Note that the "factor" referred to below is a play speed factor
+// (higher = faster, 1.0 = normal speed), the "value" is a percentage
+// (higher = faster, 100 = normal speed), and the "position" is an
+// integer step on the dial's scale (0-120, 60 = centre).
+
+PlaySpeedRangeMapper::PlaySpeedRangeMapper() :
+    m_minpos(0),
+    m_maxpos(120)
+{
+}
+
+int
+PlaySpeedRangeMapper::getPositionForValue(double value) const
+{
+    // value is percent
+    double factor = getFactorForValue(value);
+    int position = getPositionForFactor(factor);
+    return position;
+}
+
+int
+PlaySpeedRangeMapper::getPositionForValueUnclamped(double value) const
+{
+    // We don't really provide this
+    return getPositionForValue(value);
+}
+
+double
+PlaySpeedRangeMapper::getValueForPosition(int position) const
+{
+    double factor = getFactorForPosition(position);
+    double pc = getValueForFactor(factor);
+    return pc;
+}
+
+double
+PlaySpeedRangeMapper::getValueForPositionUnclamped(int position) const
+{
+    // We don't really provide this
+    return getValueForPosition(position);
+}
+
+double
+PlaySpeedRangeMapper::getValueForFactor(double factor) const
+{
+    return factor * 100.0;
+}
+
+double
+PlaySpeedRangeMapper::getFactorForValue(double value) const
+{
+    return value / 100.0;
+}
+
+int
+PlaySpeedRangeMapper::getPositionForFactor(double factor) const
+{
+    if (factor == 0) return m_minpos;
+    int pos = int(lrint((log2(factor) + 3.0) * 20.0));
+    if (pos < m_minpos) pos = m_minpos;
+    if (pos > m_maxpos) pos = m_maxpos;
+    return pos;
+}
+
+double
+PlaySpeedRangeMapper::getFactorForPosition(int position) const
+{
+    return pow(2.0, double(position) * 0.05 - 3.0);
+}
+
+QString
+PlaySpeedRangeMapper::getUnit() const
+{
+    return "%";
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/audio/PlaySpeedRangeMapper.h	Wed Apr 20 12:06:28 2016 +0100
@@ -0,0 +1,49 @@
+/* -*- 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 getMinPosition() const { return m_minpos; }
+    int getMaxPosition() const { return m_maxpos; }
+    
+    virtual int getPositionForValue(double value) const;
+    virtual int getPositionForValueUnclamped(double value) const;
+
+    virtual double getValueForPosition(int position) const;
+    virtual double getValueForPositionUnclamped(int position) const;
+
+    int getPositionForFactor(double factor) const;
+    double getValueForFactor(double factor) const;
+
+    double getFactorForPosition(int position) const;
+    double getFactorForValue(double value) const;
+
+    virtual QString getUnit() const;
+    
+protected:
+    int m_minpos;
+    int m_maxpos;
+};
+
+
+#endif
--- a/audioio/AudioCallbackPlaySource.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1870 +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 "base/ViewManagerBase.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 "AudioCallbackPlayTarget.h"
-
-#include <rubberband/RubberBandStretcher.h>
-using namespace RubberBand;
-
-#include <iostream>
-#include <cassert>
-
-//#define DEBUG_AUDIO_PLAY_SOURCE 1
-//#define DEBUG_AUDIO_PLAY_SOURCE_PLAYING 1
-
-static const size_t DEFAULT_RING_BUFFER_SIZE = 131071;
-
-AudioCallbackPlaySource::AudioCallbackPlaySource(ViewManagerBase *manager,
-                                                 QString clientName) :
-    m_viewManager(manager),
-    m_audioGenerator(new AudioGenerator()),
-    m_clientName(clientName),
-    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_target(0),
-    m_lastRetrievalTimestamp(0.0),
-    m_lastRetrievedBlockSize(0),
-    m_trustworthyTimestamps(true),
-    m_lastCurrentFrame(0),
-    m_playing(false),
-    m_exiting(false),
-    m_lastModelEndFrame(0),
-    m_ringBufferSize(DEFAULT_RING_BUFFER_SIZE),
-    m_outputLeft(0.0),
-    m_outputRight(0.0),
-    m_auditioningPlugin(0),
-    m_auditioningPluginBypassed(false),
-    m_playStartFrame(0),
-    m_playStartFramePassed(false),
-    m_timeStretcher(0),
-    m_monoStretcher(0),
-    m_stretchRatio(1.0),
-    m_stretcherInputCount(0),
-    m_stretcherInputs(0),
-    m_stretcherInputSizes(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(this, SIGNAL(playStatusChanged(bool)),
-            m_viewManager, SLOT(playStatusChanged(bool)));
-
-    connect(PlayParameterRepository::getInstance(),
-	    SIGNAL(playParametersChanged(PlayParameters *)),
-	    this, SLOT(playParametersChanged(PlayParameters *)));
-
-    connect(Preferences::getInstance(),
-            SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
-            this, SLOT(preferenceChanged(PropertyContainer::PropertyName)));
-}
-
-AudioCallbackPlaySource::~AudioCallbackPlaySource()
-{
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    SVDEBUG << "AudioCallbackPlaySource::~AudioCallbackPlaySource entering" << endl;
-#endif
-    m_exiting = true;
-
-    if (m_fillThread) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource dtor: awakening thread" << endl;
-#endif
-        m_condition.wakeAll();
-	m_fillThread->wait();
-	delete m_fillThread;
-    }
-
-    clearModels();
-    
-    if (m_readBuffers != m_writeBuffers) {
-	delete m_readBuffers;
-    }
-
-    delete m_writeBuffers;
-
-    delete m_audioGenerator;
-
-    for (size_t i = 0; i < m_stretcherInputCount; ++i) {
-        delete[] m_stretcherInputs[i];
-    }
-    delete[] m_stretcherInputSizes;
-    delete[] m_stretcherInputs;
-
-    delete m_timeStretcher;
-    delete m_monoStretcher;
-
-    m_bufferScavenger.scavenge(true);
-    m_pluginScavenger.scavenge(true);
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    SVDEBUG << "AudioCallbackPlaySource::~AudioCallbackPlaySource finishing" << endl;
-#endif
-}
-
-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
-    cout << "Adding model with " << modelChannels << " channels at rate " << model->getSampleRate() << 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) {
-                    SVDEBUG << "AudioCallbackPlaySource::addModel: Conflicting wave file model " << *i << " found" << endl;
-                    conflicting = true;
-                    break;
-                }
-            }
-
-            if (conflicting) {
-
-                SVDEBUG << "AudioCallbackPlaySource::addModel: ERROR: "
-                          << "New model sample rate does not match" << endl
-                          << "existing model(s) (new " << model->getSampleRate()
-                          << " vs " << m_sourceSampleRate
-                          << "), playback will be wrong"
-                          << 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;
-	}
-    }
-
-    rebuildRangeLists();
-
-    m_mutex.unlock();
-
-    m_audioGenerator->setTargetChannelCount(getTargetChannelCount());
-
-    if (!m_fillThread) {
-	m_fillThread = new FillThread(*this);
-	m_fillThread->start();
-    }
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource::addModel: now have " << m_models.size() << " model(s) -- emitting modelReplaced" << endl;
-#endif
-
-    if (buffersChanged || srChanged) {
-	emit modelReplaced();
-    }
-
-    connect(model, SIGNAL(modelChanged(size_t, size_t)),
-            this, SLOT(modelChanged(size_t, size_t)));
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource::addModel: awakening thread" << endl;
-#endif
-
-    m_condition.wakeAll();
-}
-
-void
-AudioCallbackPlaySource::modelChanged(size_t startFrame, size_t endFrame)
-{
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    SVDEBUG << "AudioCallbackPlaySource::modelChanged(" << startFrame << "," << endFrame << ")" << endl;
-#endif
-    if (endFrame > m_lastModelEndFrame) {
-        m_lastModelEndFrame = endFrame;
-        rebuildRangeLists();
-    }
-}
-
-void
-AudioCallbackPlaySource::removeModel(Model *model)
-{
-    m_mutex.lock();
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource::removeModel(" << model << ")" << 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) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	cout << "AudioCallbackPlaySource::removeModel(" << model << "): checking end frame on model " << *i << endl;
-#endif
-	if ((*i)->getEndFrame() > lastEnd) lastEnd = (*i)->getEndFrame();
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	cout << "(done, lastEnd now " << lastEnd << ")" << endl;
-#endif
-    }
-    m_lastModelEndFrame = lastEnd;
-
-    m_audioGenerator->removeModel(model);
-
-    m_mutex.unlock();
-
-    clearRingBuffers();
-}
-
-void
-AudioCallbackPlaySource::clearModels()
-{
-    m_mutex.lock();
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource::clearModels()" << 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();
-
-    clearRingBuffers();
-}    
-
-void
-AudioCallbackPlaySource::clearRingBuffers(bool haveLock, size_t count)
-{
-    if (!haveLock) m_mutex.lock();
-
-    rebuildRangeLists();
-
-    if (count == 0) {
-	if (m_writeBuffers) count = m_writeBuffers->size();
-    }
-
-    m_writeBufferFill = getCurrentBufferedFrame();
-
-    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));
-    }
-
-//    cout << "AudioCallbackPlaySource::clearRingBuffers: Created "
-//	      << count << " write buffers" << endl;
-
-    if (!haveLock) {
-	m_mutex.unlock();
-    }
-}
-
-void
-AudioCallbackPlaySource::play(size_t startFrame)
-{
-    if (m_viewManager->getPlaySelectionMode() &&
-	!m_viewManager->getSelections().empty()) {
-
-        SVDEBUG << "AudioCallbackPlaySource::play: constraining frame " << startFrame << " to selection = ";
-
-        startFrame = m_viewManager->constrainFrameToSelection(startFrame);
-
-        SVDEBUG << startFrame << endl;
-
-    } else {
-	if (startFrame >= m_lastModelEndFrame) {
-	    startFrame = 0;
-	}
-    }
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cerr << "play(" << startFrame << ") -> playback model ";
-#endif
-
-    startFrame = m_viewManager->alignReferenceToPlaybackFrame(startFrame);
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cerr << startFrame << endl;
-#endif
-
-    // 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.
-    // NO -- we can end up playing some first -- always reset here
-
-    m_mutex.lock();
-
-    if (m_timeStretcher) {
-        m_timeStretcher->reset();
-    }
-    if (m_monoStretcher) {
-        m_monoStretcher->reset();
-    }
-
-    m_readBufferFill = m_writeBufferFill = startFrame;
-    if (m_readBuffers) {
-        for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-            RingBuffer<float> *rb = getReadRingBuffer(c);
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            cerr << "reset ring buffer for channel " << c << endl;
-#endif
-            if (rb) rb->reset();
-        }
-    }
-    if (m_converter) src_reset(m_converter);
-    if (m_crapConverter) src_reset(m_crapConverter);
-
-    m_mutex.unlock();
-
-    m_audioGenerator->reset();
-
-    m_playStartFrame = startFrame;
-    m_playStartFramePassed = false;
-    m_playStartedAt = RealTime::zeroTime;
-    if (m_target) {
-        m_playStartedAt = RealTime::fromSeconds(m_target->getCurrentTime());
-    }
-
-    bool changed = !m_playing;
-    m_lastRetrievalTimestamp = 0;
-    m_lastCurrentFrame = 0;
-    m_playing = true;
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource::play: awakening thread" << endl;
-#endif
-
-    m_condition.wakeAll();
-    if (changed) {
-        emit playStatusChanged(m_playing);
-        emit activity(tr("Play from %1").arg
-                      (RealTime::frame2RealTime
-                       (m_playStartFrame, m_sourceSampleRate).toText().c_str()));
-    }
-}
-
-void
-AudioCallbackPlaySource::stop()
-{
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    SVDEBUG << "AudioCallbackPlaySource::stop()" << endl;
-#endif
-    bool changed = m_playing;
-    m_playing = false;
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource::stop: awakening thread" << endl;
-#endif
-
-    m_condition.wakeAll();
-    m_lastRetrievalTimestamp = 0;
-    if (changed) {
-        emit playStatusChanged(m_playing);
-        emit activity(tr("Stop at %1").arg
-                      (RealTime::frame2RealTime
-                       (m_lastCurrentFrame, m_sourceSampleRate).toText().c_str()));
-    }
-    m_lastCurrentFrame = 0;
-}
-
-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()
-{
-    cerr << "Audio processing overload!" << endl;
-
-    if (!m_playing) return;
-
-    RealTimePluginInstance *ap = m_auditioningPlugin;
-    if (ap && !m_auditioningPluginBypassed) {
-        m_auditioningPluginBypassed = true;
-        emit audioOverloadPluginDisabled();
-        return;
-    }
-
-    if (m_timeStretcher &&
-        m_timeStretcher->getTimeRatio() < 1.0 &&
-        m_stretcherInputCount > 1 &&
-        m_monoStretcher && !m_stretchMono) {
-        m_stretchMono = true;
-        emit audioTimeStretchMultiChannelDisabled();
-        return;
-    }
-}
-
-void
-AudioCallbackPlaySource::setTarget(AudioCallbackPlayTarget *target, size_t size)
-{
-    m_target = target;
-    cout << "AudioCallbackPlaySource::setTarget: Block size -> " << size << endl;
-    if (size != 0) {
-        m_blockSize = size;
-    }
-    if (size * 4 > m_ringBufferSize) {
-        SVDEBUG << "AudioCallbackPlaySource::setTarget: Buffer size "
-                  << size << " > a quarter of ring buffer size "
-                  << m_ringBufferSize << ", calling for more ring buffer"
-                  << endl;
-        m_ringBufferSize = size * 4;
-        if (m_writeBuffers && !m_writeBuffers->empty()) {
-            clearRingBuffers();
-        }
-    }
-}
-
-size_t
-AudioCallbackPlaySource::getTargetBlockSize() const
-{
-//    cout << "AudioCallbackPlaySource::getTargetBlockSize() -> " << m_blockSize << 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()
-{
-    // This method attempts to estimate which audio sample frame is
-    // "currently coming through the speakers".
-
-    size_t targetRate = getTargetSampleRate();
-    size_t latency = m_playLatency; // at target rate
-    RealTime latency_t = RealTime::frame2RealTime(latency, targetRate);
-
-    return getCurrentFrame(latency_t);
-}
-
-size_t
-AudioCallbackPlaySource::getCurrentBufferedFrame()
-{
-    return getCurrentFrame(RealTime::zeroTime);
-}
-
-size_t
-AudioCallbackPlaySource::getCurrentFrame(RealTime latency_t)
-{
-    bool resample = false;
-    double resampleRatio = 1.0;
-
-    // We resample when filling the ring buffer, and time-stretch when
-    // draining it.  The buffer contains data at the "target rate" and
-    // the latency provided by the target is also at the target rate.
-    // Because of the multiple rates involved, we do the actual
-    // calculation using RealTime instead.
-
-    size_t sourceRate = getSourceSampleRate();
-    size_t targetRate = getTargetSampleRate();
-
-    if (sourceRate == 0 || targetRate == 0) return 0;
-
-    size_t inbuffer = 0; // at target rate
-
-    for (size_t c = 0; c < getTargetChannelCount(); ++c) {
-	RingBuffer<float> *rb = getReadRingBuffer(c);
-	if (rb) {
-	    size_t here = rb->getReadSpace();
-	    if (c == 0 || here < inbuffer) inbuffer = here;
-	}
-    }
-
-    size_t readBufferFill = m_readBufferFill;
-    size_t lastRetrievedBlockSize = m_lastRetrievedBlockSize;
-    double lastRetrievalTimestamp = m_lastRetrievalTimestamp;
-    double currentTime = 0.0;
-    if (m_target) currentTime = m_target->getCurrentTime();
-
-    bool looping = m_viewManager->getPlayLoopMode();
-
-    RealTime inbuffer_t = RealTime::frame2RealTime(inbuffer, targetRate);
-
-    size_t stretchlat = 0;
-    double timeRatio = 1.0;
-
-    if (m_timeStretcher) {
-        stretchlat = m_timeStretcher->getLatency();
-        timeRatio = m_timeStretcher->getTimeRatio();
-    }
-
-    RealTime stretchlat_t = RealTime::frame2RealTime(stretchlat, targetRate);
-
-    // When the target has just requested a block from us, the last
-    // sample it obtained was our buffer fill frame count minus the
-    // amount of read space (converted back to source sample rate)
-    // remaining now.  That sample is not expected to be played until
-    // the target's play latency has elapsed.  By the time the
-    // following block is requested, that sample will be at the
-    // target's play latency minus the last requested block size away
-    // from being played.
-
-    RealTime sincerequest_t = RealTime::zeroTime;
-    RealTime lastretrieved_t = RealTime::zeroTime;
-
-    if (m_target &&
-        m_trustworthyTimestamps &&
-        lastRetrievalTimestamp != 0.0) {
-
-        lastretrieved_t = RealTime::frame2RealTime
-            (lastRetrievedBlockSize, targetRate);
-
-        // calculate number of frames at target rate that have elapsed
-        // since the end of the last call to getSourceSamples
-
-        if (m_trustworthyTimestamps && !looping) {
-
-            // this adjustment seems to cause more problems when looping
-            double elapsed = currentTime - lastRetrievalTimestamp;
-
-            if (elapsed > 0.0) {
-                sincerequest_t = RealTime::fromSeconds(elapsed);
-            }
-        }
-
-    } else {
-
-        lastretrieved_t = RealTime::frame2RealTime
-            (getTargetBlockSize(), targetRate);
-    }
-
-    RealTime bufferedto_t = RealTime::frame2RealTime(readBufferFill, sourceRate);
-
-    if (timeRatio != 1.0) {
-        lastretrieved_t = lastretrieved_t / timeRatio;
-        sincerequest_t = sincerequest_t / timeRatio;
-        latency_t = latency_t / timeRatio;
-    }
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-    cerr << "\nbuffered to: " << bufferedto_t << ", in buffer: " << inbuffer_t << ", time ratio " << timeRatio << "\n  stretcher latency: " << stretchlat_t << ", device latency: " << latency_t << "\n  since request: " << sincerequest_t << ", last retrieved quantity: " << lastretrieved_t << endl;
-#endif
-
-    RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate);
-
-    // Normally the range lists should contain at least one item each
-    // -- if playback is unconstrained, that item should report the
-    // entire source audio duration.
-
-    if (m_rangeStarts.empty()) {
-        rebuildRangeLists();
-    }
-
-    if (m_rangeStarts.empty()) {
-        // this code is only used in case of error in rebuildRangeLists
-        RealTime playing_t = bufferedto_t
-            - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t
-            + sincerequest_t;
-        if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime;
-        size_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
-        return m_viewManager->alignPlaybackFrameToReference(frame);
-    }
-
-    int inRange = 0;
-    int index = 0;
-
-    for (size_t i = 0; i < m_rangeStarts.size(); ++i) {
-        if (bufferedto_t >= m_rangeStarts[i]) {
-            inRange = index;
-        } else {
-            break;
-        }
-        ++index;
-    }
-
-    if (inRange >= m_rangeStarts.size()) inRange = m_rangeStarts.size()-1;
-
-    RealTime playing_t = bufferedto_t;
-
-    playing_t = playing_t
-        - latency_t - stretchlat_t - lastretrieved_t - inbuffer_t
-        + sincerequest_t;
-
-    // This rather gross little hack is used to ensure that latency
-    // compensation doesn't result in the playback pointer appearing
-    // to start earlier than the actual playback does.  It doesn't
-    // work properly (hence the bail-out in the middle) because if we
-    // are playing a relatively short looped region, the playing time
-    // estimated from the buffer fill frame may have wrapped around
-    // the region boundary and end up being much smaller than the
-    // theoretical play start frame, perhaps even for the entire
-    // duration of playback!
-
-    if (!m_playStartFramePassed) {
-        RealTime playstart_t = RealTime::frame2RealTime(m_playStartFrame,
-                                                        sourceRate);
-        if (playing_t < playstart_t) {
-//            cerr << "playing_t " << playing_t << " < playstart_t " 
-//                      << playstart_t << endl;
-            if (/*!!! sincerequest_t > RealTime::zeroTime && */
-                m_playStartedAt + latency_t + stretchlat_t <
-                RealTime::fromSeconds(currentTime)) {
-//                cerr << "but we've been playing for long enough that I think we should disregard it (it probably results from loop wrapping)" << endl;
-                m_playStartFramePassed = true;
-            } else {
-                playing_t = playstart_t;
-            }
-        } else {
-            m_playStartFramePassed = true;
-        }
-    }
- 
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-    cerr << "playing_t " << playing_t;
-#endif
-
-    playing_t = playing_t - m_rangeStarts[inRange];
- 
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-    cerr << " as offset into range " << inRange << " (start =" << m_rangeStarts[inRange] << " duration =" << m_rangeDurations[inRange] << ") = " << playing_t << endl;
-#endif
-
-    while (playing_t < RealTime::zeroTime) {
-
-        if (inRange == 0) {
-            if (looping) {
-                inRange = m_rangeStarts.size() - 1;
-            } else {
-                break;
-            }
-        } else {
-            --inRange;
-        }
-
-        playing_t = playing_t + m_rangeDurations[inRange];
-    }
-
-    playing_t = playing_t + m_rangeStarts[inRange];
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-    cerr << "  playing time: " << playing_t << endl;
-#endif
-
-    if (!looping) {
-        if (inRange == m_rangeStarts.size()-1 &&
-            playing_t >= m_rangeStarts[inRange] + m_rangeDurations[inRange]) {
-cerr << "Not looping, inRange " << inRange << " == rangeStarts.size()-1, playing_t " << playing_t << " >= m_rangeStarts[inRange] " << m_rangeStarts[inRange] << " + m_rangeDurations[inRange] " << m_rangeDurations[inRange] << " -- stopping" << endl;
-            stop();
-        }
-    }
-
-    if (playing_t < RealTime::zeroTime) playing_t = RealTime::zeroTime;
-
-    size_t frame = RealTime::realTime2Frame(playing_t, sourceRate);
-
-    if (m_lastCurrentFrame > 0 && !looping) {
-        if (frame < m_lastCurrentFrame) {
-            frame = m_lastCurrentFrame;
-        }
-    }
-
-    m_lastCurrentFrame = frame;
-
-    return m_viewManager->alignPlaybackFrameToReference(frame);
-}
-
-void
-AudioCallbackPlaySource::rebuildRangeLists()
-{
-    bool constrained = (m_viewManager->getPlaySelectionMode());
-
-    m_rangeStarts.clear();
-    m_rangeDurations.clear();
-
-    size_t sourceRate = getSourceSampleRate();
-    if (sourceRate == 0) return;
-
-    RealTime end = RealTime::frame2RealTime(m_lastModelEndFrame, sourceRate);
-    if (end == RealTime::zeroTime) return;
-
-    if (!constrained) {
-        m_rangeStarts.push_back(RealTime::zeroTime);
-        m_rangeDurations.push_back(end);
-        return;
-    }
-
-    MultiSelection::SelectionList selections = m_viewManager->getSelections();
-    MultiSelection::SelectionList::const_iterator i;
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    SVDEBUG << "AudioCallbackPlaySource::rebuildRangeLists" << endl;
-#endif
-
-    if (!selections.empty()) {
-
-        for (i = selections.begin(); i != selections.end(); ++i) {
-            
-            RealTime start =
-                (RealTime::frame2RealTime
-                 (m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()),
-                  sourceRate));
-            RealTime duration = 
-                (RealTime::frame2RealTime
-                 (m_viewManager->alignReferenceToPlaybackFrame(i->getEndFrame()) -
-                  m_viewManager->alignReferenceToPlaybackFrame(i->getStartFrame()),
-                  sourceRate));
-            
-            m_rangeStarts.push_back(start);
-            m_rangeDurations.push_back(duration);
-        }
-    } else {
-        m_rangeStarts.push_back(RealTime::zeroTime);
-        m_rangeDurations.push_back(end);
-    }
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cerr << "Now have " << m_rangeStarts.size() << " play ranges" << endl;
-#endif
-}
-
-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)
-{
-    bool first = (m_targetSampleRate == 0);
-
-    m_targetSampleRate = sr;
-    initialiseConverter();
-
-    if (first && (m_stretchRatio != 1.f)) {
-        // couldn't create a stretcher before because we had no sample
-        // rate: make one now
-        setTimeStretch(m_stretchRatio);
-    }
-}
-
-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) {
-	    cerr
-		<< "AudioCallbackPlaySource::setModel: ERROR in creating samplerate converter: "
-		<< src_strerror(err) << 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
-    SVDEBUG << "AudioCallbackPlaySource::setResampleQuality: setting to "
-              << m_resampleQuality << endl;
-#endif
-
-    initialiseConverter();
-}
-
-void
-AudioCallbackPlaySource::setAuditioningEffect(Auditionable *a)
-{
-    RealTimePluginInstance *plugin = dynamic_cast<RealTimePluginInstance *>(a);
-    if (a && !plugin) {
-        cerr << "WARNING: AudioCallbackPlaySource::setAuditioningEffect: auditionable object " << a << " is not a real-time plugin instance" << endl;
-    }
-
-    m_mutex.lock();
-    m_auditioningPlugin = plugin;
-    m_auditioningPluginBypassed = false;
-    m_mutex.unlock();
-}
-
-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)
-{
-    m_stretchRatio = factor;
-
-    if (!getTargetSampleRate()) return; // have to make our stretcher later
-
-    if (m_timeStretcher || (factor == 1.f)) {
-        // stretch ratio will be set in next process call if appropriate
-    } else {
-        m_stretcherInputCount = getTargetChannelCount();
-        RubberBandStretcher *stretcher = new RubberBandStretcher
-            (getTargetSampleRate(),
-             m_stretcherInputCount,
-             RubberBandStretcher::OptionProcessRealTime,
-             factor);
-        RubberBandStretcher *monoStretcher = new RubberBandStretcher
-            (getTargetSampleRate(),
-             1,
-             RubberBandStretcher::OptionProcessRealTime,
-             factor);
-        m_stretcherInputs = new float *[m_stretcherInputCount];
-        m_stretcherInputSizes = new size_t[m_stretcherInputCount];
-        for (size_t c = 0; c < m_stretcherInputCount; ++c) {
-            m_stretcherInputSizes[c] = 16384;
-            m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]];
-        }
-        m_monoStretcher = monoStretcher;
-        m_timeStretcher = stretcher;
-    }
-
-    emit activity(tr("Change time-stretch factor to %1").arg(factor));
-}
-
-size_t
-AudioCallbackPlaySource::getSourceSamples(size_t ucount, float **buffer)
-{
-    int count = ucount;
-
-    if (!m_playing) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-        SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Not playing" << endl;
-#endif
-	for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
-	    for (int i = 0; i < count; ++i) {
-		buffer[ch][i] = 0.0;
-	    }
-	}
-	return 0;
-    }
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-    SVDEBUG << "AudioCallbackPlaySource::getSourceSamples: Playing" << endl;
-#endif
-
-    // 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) {
-            cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: "
-                      << "No ring buffer available for channel " << ch
-                      << ", returning no data here" << endl;
-            count = 0;
-            break;
-        }
-
-        size_t rs = rb->getReadSpace();
-        if (rs < count) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: "
-                      << "Ring buffer for channel " << ch << " has only "
-                      << rs << " (of " << count << ") samples available ("
-                      << "ring buffer size is " << rb->getSize() << ", write "
-                      << "space " << rb->getWriteSpace() << "), "
-                      << "reducing request size" << endl;
-#endif
-            count = rs;
-        }
-    }
-
-    if (count == 0) return 0;
-
-    RubberBandStretcher *ts = m_timeStretcher;
-    RubberBandStretcher *ms = m_monoStretcher;
-
-    float ratio = ts ? ts->getTimeRatio() : 1.f;
-
-    if (ratio != m_stretchRatio) {
-        if (!ts) {
-            cerr << "WARNING: AudioCallbackPlaySource::getSourceSamples: Time ratio change to " << m_stretchRatio << " is pending, but no stretcher is set" << endl;
-            m_stretchRatio = 1.f;
-        } else {
-            ts->setTimeRatio(m_stretchRatio);
-            if (ms) ms->setTimeRatio(m_stretchRatio);
-            if (m_stretchRatio >= 1.0) m_stretchMono = false;
-        }
-    }
-
-    int stretchChannels = m_stretcherInputCount;
-    if (m_stretchMono) {
-        if (ms) {
-            ts = ms;
-            stretchChannels = 1;
-        } else {
-            m_stretchMono = false;
-        }
-    }
-
-    if (m_target) {
-        m_lastRetrievedBlockSize = count;
-        m_lastRetrievalTimestamp = m_target->getCurrentTime();
-    }
-
-    if (!ts || ratio == 1.f) {
-
-	int 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
-		cout << "AudioCallbackPlaySource::getSamples: got " << got << " (of " << count << ") samples on channel " << ch << ", signalling for more (possibly)" << endl;
-#endif
-	    }
-
-	    for (size_t ch = 0; ch < getTargetChannelCount(); ++ch) {
-		for (int i = got; i < count; ++i) {
-		    buffer[ch][i] = 0.0;
-		}
-	    }
-	}
-
-        applyAuditioningEffect(count, buffer);
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource::getSamples: awakening thread" << endl;
-#endif
-
-        m_condition.wakeAll();
-
-	return got;
-    }
-
-    size_t channels = getTargetChannelCount();
-    size_t available;
-    int warned = 0;
-    size_t fedToStretcher = 0;
-
-    // The input block for a given output is approx output / ratio,
-    // but we can't predict it exactly, for an adaptive timestretcher.
-
-    while ((available = ts->available()) < count) {
-
-        size_t reqd = lrintf((count - available) / ratio);
-        reqd = std::max(reqd, ts->getSamplesRequired());
-        if (reqd == 0) reqd = 1;
-                
-        size_t got = reqd;
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-        cerr << "reqd = " <<reqd << ", channels = " << channels << ", ic = " << m_stretcherInputCount << endl;
-#endif
-
-        for (size_t c = 0; c < channels; ++c) {
-            if (c >= m_stretcherInputCount) continue;
-            if (reqd > m_stretcherInputSizes[c]) {
-                if (c == 0) {
-                    cerr << "WARNING: resizing stretcher input buffer from " << m_stretcherInputSizes[c] << " to " << (reqd * 2) << endl;
-                }
-                delete[] m_stretcherInputs[c];
-                m_stretcherInputSizes[c] = reqd * 2;
-                m_stretcherInputs[c] = new float[m_stretcherInputSizes[c]];
-            }
-        }
-
-        for (size_t c = 0; c < channels; ++c) {
-            if (c >= m_stretcherInputCount) continue;
-            RingBuffer<float> *rb = getReadRingBuffer(c);
-            if (rb) {
-                size_t gotHere;
-                if (stretchChannels == 1 && c > 0) {
-                    gotHere = rb->readAdding(m_stretcherInputs[0], got);
-                } else {
-                    gotHere = rb->read(m_stretcherInputs[c], got);
-                }
-                if (gotHere < got) got = gotHere;
-                
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-                if (c == 0) {
-                    SVDEBUG << "feeding stretcher: got " << gotHere
-                              << ", " << rb->getReadSpace() << " remain" << endl;
-                }
-#endif
-                
-            } else {
-                cerr << "WARNING: No ring buffer available for channel " << c << " in stretcher input block" << endl;
-            }
-        }
-
-        if (got < reqd) {
-            cerr << "WARNING: Read underrun in playback ("
-                      << got << " < " << reqd << ")" << endl;
-        }
-
-        ts->process(m_stretcherInputs, got, false);
-
-        fedToStretcher += got;
-
-        if (got == 0) break;
-
-        if (ts->available() == available) {
-            cerr << "WARNING: AudioCallbackPlaySource::getSamples: Added " << got << " samples to time stretcher, created no new available output samples (warned = " << warned << ")" << endl;
-            if (++warned == 5) break;
-        }
-    }
-
-    ts->retrieve(buffer, count);
-
-    for (int c = stretchChannels; c < getTargetChannelCount(); ++c) {
-        for (int i = 0; i < count; ++i) {
-            buffer[c][i] = buffer[0][i];
-        }
-    }
-
-    applyAuditioningEffect(count, buffer);
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySource::getSamples [stretched]: awakening thread" << endl;
-#endif
-
-    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()) {
-//        cerr << "plugin input count " << plugin->getAudioInputCount() 
-//                  << " != our channel count " << getTargetChannelCount()
-//                  << endl;
-        return;
-    }
-    if (plugin->getAudioOutputCount() != getTargetChannelCount()) {
-//        cerr << "plugin output count " << plugin->getAudioOutputCount() 
-//                  << " != our channel count " << getTargetChannelCount()
-//                  << endl;
-        return;
-    }
-    if (plugin->getBufferSize() < count) {
-//        cerr << "plugin buffer size " << plugin->getBufferSize() 
-//                  << " < our block size " << count
-//                  << 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, count);
-    
-    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) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-        cout << "AudioCallbackPlaySourceFillThread: no space to fill" << endl;
-#endif
-        return false;
-    }
-
-    size_t f = m_writeBufferFill;
-	
-    bool readWriteEqual = (m_readBuffers == m_writeBuffers);
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    if (!readWriteEqual) {
-        cout << "AudioCallbackPlaySourceFillThread: note read buffers != write buffers" << endl;
-    }
-    cout << "AudioCallbackPlaySourceFillThread: filling " << space << " frames" << endl;
-#endif
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "buffered to " << f << " already" << endl;
-#endif
-
-    bool resample = (getSourceSampleRate() != getTargetSampleRate());
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << (resample ? "" : "not ") << "resampling (source " << getSourceSampleRate() << ", target " << getTargetSampleRate() << ")" << 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) {
-	    cerr << "WARNING: sample rates differ, but no converter available!" << 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); // also modifies f
-
-	// 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->getTimeRatio() < 0.4) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            cout << "Using crappy converter" << 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) {
-	    cerr
-		<< "AudioCallbackPlaySourceFillThread: ERROR in samplerate conversion: "
-		<< src_strerror(err) << endl;
-	    //!!! Then what?
-	} else {
-	    got = data.input_frames_used;
-	    toCopy = data.output_frames_gen;
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	    cout << "Resampled " << got << " frames to " << toCopy << " frames" << 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
-        size_t reqSpace = space;
-	space = (reqSpace / generatorBlockSize) * generatorBlockSize;
-	if (space == 0) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            cout << "requested fill of " << reqSpace
-                      << " is less than generator block size of "
-                      << generatorBlockSize << ", leaving it" << endl;
-#endif
-            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); // also modifies f
-
-	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
-		cout << "Wrote " << actual << " samples for ch " << c << ", now "
-			  << wb->getReadSpace() << " to read" 
-			  << endl;
-#endif
-                if (actual < got) {
-                    cerr << "WARNING: Buffer overrun in channel " << c
-                              << ": wrote " << actual << " of " << got
-                              << " samples" << endl;
-                }
-            }
-	}
-
-	m_writeBufferFill = f;
-	if (readWriteEqual) m_readBufferFill = f;
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-        cout << "Read buffer fill is now " << m_readBufferFill << endl;
-#endif
-
-	//!!! 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
-    cout << "Selection playback: start " << frame << ", size " << count <<", channels " << channels << 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) {
-
-            size_t rChunkStart =
-                m_viewManager->alignPlaybackFrameToReference(chunkStart);
-	    
-	    Selection selection =
-		m_viewManager->getContainingSelection(rChunkStart, true);
-	    
-	    if (selection.isEmpty()) {
-		if (looping) {
-		    selection = *m_viewManager->getSelections().begin();
-		    chunkStart = m_viewManager->alignReferenceToPlaybackFrame
-                        (selection.getStartFrame());
-		    fadeIn = 50;
-		}
-	    }
-
-	    if (selection.isEmpty()) {
-
-		chunkSize = 0;
-		nextChunkStart = chunkStart;
-
-	    } else {
-
-                size_t sf = m_viewManager->alignReferenceToPlaybackFrame
-                    (selection.getStartFrame());
-                size_t ef = m_viewManager->alignReferenceToPlaybackFrame
-                    (selection.getEndFrame());
-
-		selectionSize = ef - sf;
-
-		if (chunkStart < sf) {
-		    chunkStart = sf;
-		    fadeIn = 50;
-		}
-
-		nextChunkStart = chunkStart + chunkSize;
-
-		if (nextChunkStart >= ef) {
-		    nextChunkStart = ef;
-		    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;
-	}
-	
-//	cout << "chunkStart " << chunkStart << ", chunkSize " << chunkSize << ", nextChunkStart " << nextChunkStart << ", frame " << frame << ", count " << count << ", processed " << processed << endl;
-
-	if (!chunkSize) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	    cout << "Ending selection playback at " << nextChunkStart << 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
-	cout << "Selection playback: chunk at " << chunkStart << " -> " << nextChunkStart << " (size " << chunkSize << ")" << 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
-    cout << "Returning selection playback " << processed << " frames to " << nextChunkStart << 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
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-                    SVDEBUG << "AudioCallbackPlaySource::unifyRingBuffers: Not unifying: write buffer has less (" << wb->getReadSpace() << ") than " << m_blockSize*2 << " to read and write buffer fill (" << m_writeBufferFill << ") is not close to end frame (" << m_lastModelEndFrame << ")" << endl;
-#endif
-		    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
-//	cout << "rs = " << rs << endl;
-	if (rs < rf) rf -= rs;
-	else rf = 0;
-    }
-    
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-    SVDEBUG << "AudioCallbackPlaySource::unifyRingBuffers: m_readBufferFill = " << m_readBufferFill << ", rf = " << rf << ", m_writeBufferFill = " << m_writeBufferFill << endl;
-#endif
-
-    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();
-//		cout << "wrs = " << wrs << endl;
-
-		if (wrs < wf) wf -= wrs;
-		else wf = 0;
-//		cout << "wf = " << wf << endl;
-		
-		if (wf < rf) skip = rf - wf;
-		if (skip == 0) break;
-	    }
-
-//	    cout << "skipping " << skip << endl;
-	    wb->skip(skip);
-	}
-    }
-		    
-    m_bufferScavenger.claim(m_readBuffers);
-    m_readBuffers = m_writeBuffers;
-    m_readBufferFill = m_writeBufferFill;
-#ifdef DEBUG_AUDIO_PLAY_SOURCE_PLAYING
-    cerr << "unified" << endl;
-#endif
-}
-
-void
-AudioCallbackPlaySource::FillThread::run()
-{
-    AudioCallbackPlaySource &s(m_source);
-    
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-    cout << "AudioCallbackPlaySourceFillThread starting" << 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();
-
-	if (work && s.m_playing && s.getSourceSampleRate()) {
-	    
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	    cout << "AudioCallbackPlaySourceFillThread: not waiting" << endl;
-#endif
-
-	    s.m_mutex.unlock();
-	    s.m_mutex.lock();
-
-	} else {
-	    
-	    float ms = 100;
-	    if (s.getSourceSampleRate() > 0) {
-		ms = float(s.m_ringBufferSize) /
-                    float(s.getSourceSampleRate()) * 1000.0;
-	    }
-	    
-	    if (s.m_playing) ms /= 10;
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            if (!s.m_playing) cout << endl;
-	    cout << "AudioCallbackPlaySourceFillThread: waiting for " << ms << "ms..." << endl;
-#endif
-	    
-	    s.m_condition.wait(&s.m_mutex, size_t(ms));
-	}
-
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	cout << "AudioCallbackPlaySourceFillThread: awoken" << endl;
-#endif
-
-	work = false;
-
-	if (!s.getSourceSampleRate()) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-            cout << "AudioCallbackPlaySourceFillThread: source sample rate is zero" << endl;
-#endif
-            continue;
-        }
-
-	bool playing = s.m_playing;
-
-	if (playing && !previouslyPlaying) {
-#ifdef DEBUG_AUDIO_PLAY_SOURCE
-	    cout << "AudioCallbackPlaySourceFillThread: playback state changed, resetting" << 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	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,382 +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 "base/RealTime.h"
-
-#include <samplerate.h>
-
-#include <set>
-#include <map>
-
-namespace RubberBand {
-    class RubberBandStretcher;
-}
-
-class Model;
-class ViewManagerBase;
-class AudioGenerator;
-class PlayParameters;
-class RealTimePluginInstance;
-class AudioCallbackPlayTarget;
-
-/**
- * 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 QObject,
-				public AudioPlaySource
-{
-    Q_OBJECT
-
-public:
-    AudioCallbackPlaySource(ViewManagerBase *, QString clientName);
-    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 last frame that would come out of the speakers if we
-     * stopped playback right now.
-     */
-    virtual size_t getCurrentBufferedFrame();
-
-    /**
-     * Return the frame at which playback is expected to end (if not looping).
-     */
-    virtual size_t getPlayEndFrame() { return m_lastModelEndFrame; }
-
-    /**
-     * Set the target and the block size of the target audio device.
-     * This should be called by the target class.
-     */
-    void setTarget(AudioCallbackPlayTarget *, size_t blockSize);
-
-    /**
-     * Get the block size of the target audio device.  This may be an
-     * estimate or upper bound, if the target has a variable block
-     * size; the source should behave itself even if this value turns
-     * out to be inaccurate.
-     */
-    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).
-     */
-    void setTimeStretch(float factor);
-
-    /**
-     * 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
-     * setAuditioningEffect (depending on real-time constraints).
-     *
-     * Pass a null pointer to remove the current auditioning plugin,
-     * if any.
-     */
-    void setAuditioningEffect(Auditionable *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();
-
-    QString getClientName() const { return m_clientName; }
-
-signals:
-    void modelReplaced();
-
-    void playStatusChanged(bool isPlaying);
-
-    void sampleRateMismatch(size_t requested, size_t available, bool willResample);
-
-    void audioOverloadPluginDisabled();
-    void audioTimeStretchMultiChannelDisabled();
-
-    void activity(QString);
-
-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:
-    ViewManagerBase                  *m_viewManager;
-    AudioGenerator                   *m_audioGenerator;
-    QString                           m_clientName;
-
-    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;
-    AudioCallbackPlayTarget          *m_target;
-    double                            m_lastRetrievalTimestamp;
-    size_t                            m_lastRetrievedBlockSize;
-    bool                              m_trustworthyTimestamps;
-    size_t                            m_lastCurrentFrame;
-    bool                              m_playing;
-    bool                              m_exiting;
-    size_t                            m_lastModelEndFrame;
-    size_t                            m_ringBufferSize;
-    float                             m_outputLeft;
-    float                             m_outputRight;
-    RealTimePluginInstance           *m_auditioningPlugin;
-    bool                              m_auditioningPluginBypassed;
-    Scavenger<RealTimePluginInstance> m_pluginScavenger;
-    size_t                            m_playStartFrame;
-    bool                              m_playStartFramePassed;
-    RealTime                          m_playStartedAt;
-
-    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();
-
-    RubberBand::RubberBandStretcher *m_timeStretcher;
-    RubberBand::RubberBandStretcher *m_monoStretcher;
-    float m_stretchRatio;
-    bool m_stretchMono;
-    
-    size_t  m_stretcherInputCount;
-    float **m_stretcherInputs;
-    size_t *m_stretcherInputSizes;
-
-    // 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);
-
-    // Ranges of current selections, if play selection is active
-    std::vector<RealTime> m_rangeStarts;
-    std::vector<RealTime> m_rangeDurations;
-    void rebuildRangeLists();
-
-    size_t getCurrentFrame(RealTime outputLatency);
-
-    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	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 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	Tue Jul 14 15:04:45 2015 +0100
+++ /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 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;
-
-    virtual void shutdown() = 0;
-
-    virtual double getCurrentTime() 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	Tue Jul 14 15:04:45 2015 +0100
+++ /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	Tue Jul 14 15:04:45 2015 +0100
+++ /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	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,713 +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 <cmath>
-
-#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)
-{
-    initialiseSampleDir();
-
-    connect(PlayParameterRepository::getInstance(),
-            SIGNAL(playPluginIdChanged(const Playable *, QString)),
-            this,
-            SLOT(playPluginIdChanged(const Playable *, QString)));
-
-    connect(PlayParameterRepository::getInstance(),
-            SIGNAL(playPluginConfigurationChanged(const Playable *, QString)),
-            this,
-            SLOT(playPluginConfigurationChanged(const Playable *, QString)));
-}
-
-AudioGenerator::~AudioGenerator()
-{
-#ifdef DEBUG_AUDIO_GENERATOR
-    SVDEBUG << "AudioGenerator::~AudioGenerator" << endl;
-#endif
-}
-
-void
-AudioGenerator::initialiseSampleDir()
-{
-    if (m_sampleDir != "") return;
-
-    try {
-        m_sampleDir = TempDirectory::getInstance()->getSubDirectoryPath("samples");
-    } catch (DirectoryCreationFailed f) {
-        cerr << "WARNING: AudioGenerator::initialiseSampleDir:"
-                  << " Failed to create temporary sample directory"
-                  << 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));
-        QString target = QDir(m_sampleDir).filePath(fileName);
-
-        if (!file.copy(target)) {
-            cerr << "WARNING: AudioGenerator::getSampleDir: "
-                      << "Unable to copy " << fileName
-                      << " into temporary directory \""
-                      << m_sampleDir << "\"" << endl;
-        } else {
-            QFile tf(target);
-            tf.setPermissions(tf.permissions() |
-                              QFile::WriteOwner |
-                              QFile::WriteUser);
-        }
-    }
-}
-
-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 Playable *playable, QString)
-{
-    const Model *model = dynamic_cast<const Model *>(playable);
-    if (!model) {
-        cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable "
-                  << playable << " is not a supported model type"
-                  << endl;
-        return;
-    }
-
-    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 Playable *playable,
-                                               QString configurationXml)
-{
-//    SVDEBUG << "AudioGenerator::playPluginConfigurationChanged" << endl;
-
-    const Model *model = dynamic_cast<const Model *>(playable);
-    if (!model) {
-        cerr << "WARNING: AudioGenerator::playPluginIdChanged: playable "
-                  << playable << " is not a supported model type"
-                  << endl;
-        return;
-    }
-
-    if (m_synthMap.find(model) == m_synthMap.end()) {
-        SVDEBUG << "AudioGenerator::playPluginConfigurationChanged: We don't know about this plugin" << endl;
-        return;
-    }
-
-    RealTimePluginInstance *plugin = m_synthMap[model];
-    if (plugin) {
-        PluginXml(plugin).setParametersFromXml(configurationXml);
-    }
-}
-
-void
-AudioGenerator::setSampleDir(RealTimePluginInstance *plugin)
-{
-    if (m_sampleDir != "") {
-        plugin->configure("sampledir", m_sampleDir.toStdString());
-    }
-} 
-
-RealTimePluginInstance *
-AudioGenerator::loadPluginFor(const Model *model)
-{
-    QString pluginId, configurationXml;
-
-    const Playable *playable = model;
-    if (!playable || !playable->canPlay()) return 0;
-
-    PlayParameters *parameters =
-	PlayParameterRepository::getInstance()->getPlayParameters(playable);
-    if (parameters) {
-        pluginId = parameters->getPlayPluginId();
-        configurationXml = parameters->getPlayPluginConfiguration();
-    }
-
-    if (pluginId == "") return 0;
-
-    RealTimePluginInstance *plugin = loadPlugin(pluginId, "");
-    if (!plugin) return 0;
-
-    if (configurationXml != "") {
-        PluginXml(plugin).setParametersFromXml(configurationXml);
-        setSampleDir(plugin);
-    }
-
-    configurationXml = PluginXml(plugin).toXmlString();
-
-    if (parameters) {
-        parameters->setPlayPluginId(pluginId);
-        parameters->setPlayPluginConfiguration(configurationXml);
-    }
-
-    return plugin;
-}
-
-RealTimePluginInstance *
-AudioGenerator::loadPlugin(QString pluginId, QString program)
-{
-    RealTimePluginFactory *factory =
-	RealTimePluginFactory::instanceFor(pluginId);
-    
-    if (!factory) {
-	cerr << "Failed to get plugin factory" << endl;
-	return 0;
-    }
-	
-    RealTimePluginInstance *instance =
-	factory->instantiatePlugin
-	(pluginId, 0, 0, m_sourceSampleRate, m_pluginBlockSize, m_targetChannelCount);
-
-    if (!instance) {
-	cerr << "Failed to instantiate plugin " << pluginId << 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 != "") {
-//        cerr << "first selecting default program " << defaultProgram << endl;
-        instance->selectProgram(defaultProgram);
-    }
-    if (program != "") {
-//        cerr << "now selecting desired program " << program << 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;
-
-//    SVDEBUG << "AudioGenerator::setTargetChannelCount(" << targetChannelCount << ")" << 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) {
-	cerr << "WARNING: AudioGenerator::mixModel: No base source sample rate available" << endl;
-	return frameCount;
-    }
-
-    QMutexLocker locker(&m_mutex);
-
-    Playable *playable = model;
-    if (!playable || !playable->canPlay()) return frameCount;
-
-    PlayParameters *parameters =
-	PlayParameterRepository::getInstance()->getPlayParameters(playable);
-    if (!parameters) return frameCount;
-
-    bool playing = !parameters->isPlayMuted();
-    if (!playing) {
-#ifdef DEBUG_AUDIO_GENERATOR
-        cout << "AudioGenerator::mixModel(" << model << "): muted" << endl;
-#endif
-        return frameCount;
-    }
-
-    if (m_soloing) {
-        if (m_soloModelSet.find(model) == m_soloModelSet.end()) {
-#ifdef DEBUG_AUDIO_GENERATOR
-            cout << "AudioGenerator::mixModel(" << model << "): not one of the solo'd models" << 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);
-    }
-
-    bool synthetic = 
-        (qobject_cast<SparseOneDimensionalModel *>(model) ||
-         qobject_cast<NoteModel *>(model));
-
-    if (synthetic) {
-        return mixSyntheticNoteModel(model, 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;
-    static size_t  channelBufCount = 0;
-
-    size_t totalFrames = frames + fadeIn/2 + fadeOut/2;
-
-    size_t modelChannels = dtvm->getChannelCount();
-
-    if (channelBufSiz < totalFrames || channelBufCount < modelChannels) {
-
-        for (size_t c = 0; c < channelBufCount; ++c) {
-            delete[] channelBuffer[c];
-        }
-
-	delete[] channelBuffer;
-        channelBuffer = new float *[modelChannels];
-
-        for (size_t c = 0; c < modelChannels; ++c) {
-            channelBuffer[c] = new float[totalFrames];
-        }
-
-        channelBufCount = modelChannels;
-	channelBufSiz = totalFrames;
-    }
-
-    size_t got = 0;
-
-    if (startFrame >= fadeIn/2) {
-        got = dtvm->getData(0, modelChannels - 1,
-                            startFrame - fadeIn/2,
-                            frames + fadeOut/2 + fadeIn/2,
-                            channelBuffer);
-    } else {
-        size_t missing = fadeIn/2 - startFrame;
-
-        for (size_t c = 0; c < modelChannels; ++c) {
-            channelBuffer[c] += missing;
-        }
-
-        got = dtvm->getData(0, modelChannels - 1,
-                            startFrame,
-                            frames + fadeOut/2,
-                            channelBuffer);
-
-        for (size_t c = 0; c < modelChannels; ++c) {
-            channelBuffer[c] -= missing;
-        }
-
-        got += missing;
-    }	    
-
-    for (size_t c = 0; c < m_targetChannelCount; ++c) {
-
-	size_t sourceChannel = (c % modelChannels);
-
-//	SVDEBUG << "mixing channel " << c << " from source channel " << sourceChannel << 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;
-	    }
-	}
-
-	for (size_t i = 0; i < fadeIn/2; ++i) {
-	    float *back = buffer[c];
-	    back -= fadeIn/2;
-	    back[i] += (channelGain * channelBuffer[sourceChannel][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;
-	    }
-            float val = channelBuffer[sourceChannel][i];
-            if (i >= got) val = 0.f;
-	    buffer[c][i] += mult * val;
-	}
-    }
-
-    return got;
-}
-  
-size_t
-AudioGenerator::mixSyntheticNoteModel(Model *model,
-                                      size_t startFrame, size_t frames,
-                                      float **buffer, float gain, float pan,
-                                      size_t /* fadeIn */,
-                                      size_t /* fadeOut */)
-{
-    RealTimePluginInstance *plugin = m_synthMap[model];
-    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
-    cout << "mixModel [synthetic note]: frames " << frames
-	      << ", blocks " << blocks << endl;
-#endif
-
-    snd_seq_event_t onEv;
-    onEv.type = SND_SEQ_EVENT_NOTEON;
-    onEv.data.note.channel = 0;
-
-    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[model];
-
-    for (size_t i = 0; i < blocks; ++i) {
-
-	size_t reqStart = startFrame + i * m_pluginBlockSize;
-
-        NoteList notes = getNotes(model,
-                                  reqStart + latency,
-                                  reqStart + latency + m_pluginBlockSize);
-
-        Vamp::RealTime blockTime = Vamp::RealTime::frame2RealTime
-	    (startFrame + i * m_pluginBlockSize, m_sourceSampleRate);
-
-	for (NoteList::const_iterator ni = notes.begin();
-             ni != notes.end(); ++ni) {
-
-	    size_t noteFrame = ni->start;
-
-	    if (noteFrame >= latency) noteFrame -= latency;
-
-	    if (noteFrame < reqStart ||
-		noteFrame >= reqStart + m_pluginBlockSize) continue;
-
-	    while (noteOffs.begin() != noteOffs.end() &&
-		   noteOffs.begin()->frame <= noteFrame) {
-
-                Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		    (noteOffs.begin()->frame, m_sourceSampleRate);
-
-		offEv.data.note.note = noteOffs.begin()->pitch;
-
-#ifdef DEBUG_AUDIO_GENERATOR
-		cerr << "mixModel [synthetic]: sending note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << endl;
-#endif
-
-		plugin->sendEvent(eventTime, &offEv);
-		noteOffs.erase(noteOffs.begin());
-	    }
-
-            Vamp::RealTime eventTime = Vamp::RealTime::frame2RealTime
-		(noteFrame, m_sourceSampleRate);
-	    
-            if (ni->isMidiPitchQuantized) {
-                onEv.data.note.note = ni->midiPitch;
-            } else {
-#ifdef DEBUG_AUDIO_GENERATOR
-                cerr << "mixModel [synthetic]: non-pitch-quantized notes are not supported [yet], quantizing" << endl;
-#endif
-                onEv.data.note.note = Pitch::getPitchForFrequency(ni->frequency);
-            }
-
-            onEv.data.note.velocity = ni->velocity;
-
-	    plugin->sendEvent(eventTime, &onEv);
-
-#ifdef DEBUG_AUDIO_GENERATOR
-	    cout << "mixModel [synthetic]: note at frame " << noteFrame << ", block start " << (startFrame + i * m_pluginBlockSize) << ", resulting time " << eventTime << endl;
-#endif
-	    
-	    noteOffs.insert
-                (NoteOff(onEv.data.note.note, noteFrame + ni->duration));
-	}
-
-	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
-            cerr << "mixModel [synthetic]: sending leftover note-off event at time " << eventTime << " frame " << noteOffs.begin()->frame << " pitch " << noteOffs.begin()->pitch << 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
-	    cout << "mixModel [synthetic]: adding " << m_pluginBlockSize << " samples from plugin output " << c << 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;
-}
-
-AudioGenerator::NoteList
-AudioGenerator::getNotes(Model *model,
-                         size_t startFrame,
-                         size_t endFrame)
-{
-    NoteList notes;
-
-    SparseOneDimensionalModel *sodm = 
-        qobject_cast<SparseOneDimensionalModel *>(model);
-
-    if (sodm) {
-        
-	SparseOneDimensionalModel::PointList points =
-	    sodm->getPoints(startFrame, endFrame);
-
-	for (SparseOneDimensionalModel::PointList::iterator pli =
-		 points.begin(); pli != points.end(); ++pli) {
-
-            notes.push_back
-                (NoteData(pli->frame,
-                          m_sourceSampleRate / 6, // arbitrary short duration
-                          64,   // default pitch
-                          100)); // default velocity
-        }
-
-        return notes;
-    }
-
-    NoteModel *nm = qobject_cast<NoteModel *>(model);
-
-    if (nm) {
-        
-	NoteModel::PointList points =
-	    nm->getPoints(startFrame, endFrame);
-
-        for (NoteModel::PointList::iterator pli =
-		 points.begin(); pli != points.end(); ++pli) {
-
-	    size_t duration = pli->duration;
-            if (duration == 0 || duration == 1) {
-                duration = m_sourceSampleRate / 20;
-            }
-
-            int pitch = lrintf(pli->value);
-
-            int velocity = 100;
-            if (pli->level > 0.f && pli->level <= 1.f) {
-                velocity = lrintf(pli->level * 127);
-            }
-
-            NoteData note(pli->frame,
-                          duration,
-                          pitch,
-                          velocity);
-
-            if (nm->getScaleUnits() == "Hz") {
-                note.frequency = pli->value;
-                note.isMidiPitchQuantized = false;
-            }
-        
-            notes.push_back(note);
-        }
-
-        return notes;
-    }
-
-    return notes;
-}
-
--- a/audioio/AudioGenerator.h	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,164 +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;
-class Playable;
-
-#include <QObject>
-#include <QMutex>
-
-#include <set>
-#include <map>
-#include <vector>
-
-class AudioGenerator : public QObject
-{
-    Q_OBJECT
-
-public:
-    AudioGenerator();
-    virtual ~AudioGenerator();
-
-    /**
-     * Add a data model to be played from and initialise any necessary
-     * audio generation code.  Returns true if the model will be
-     * played.  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 Playable *, QString);
-    void playPluginConfigurationChanged(const Playable *, QString);
-
-protected:
-    size_t       m_sourceSampleRate;
-    size_t       m_targetChannelCount;
-
-    bool m_soloing;
-    std::set<Model *> m_soloModelSet;
-
-    struct NoteData {
-
-        NoteData(size_t _start, size_t _dur, int _mp, int _vel) :
-            start(_start), duration(_dur), midiPitch(_mp), frequency(0),
-            isMidiPitchQuantized(true), velocity(_vel) { };
-            
-        size_t start;     // audio sample frame
-        size_t duration;  // in audio sample frames
-        int midiPitch; // 0-127
-        int frequency; // Hz, to be used if isMidiPitchQuantized false
-        bool isMidiPitchQuantized;
-        int velocity;  // MIDI-style 0-127
-    };
-
-    typedef std::vector<NoteData> NoteList;
-    
-    struct NoteOff {
-
-        NoteOff(int _p, size_t _f) : pitch(_p), frame(_f) { }
-
-	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::multiset<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 void initialiseSampleDir();
-    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 mixSyntheticNoteModel
-    (Model *model, size_t startFrame, size_t frameCount,
-     float **buffer, float gain, float pan, size_t fadeIn, size_t fadeOut);
-    
-    NoteList getNotes(Model *model, size_t startFrame, size_t endFrame);
-
-    static const size_t m_pluginBlockSize;
-};
-
-#endif
-
--- a/audioio/AudioJACKTarget.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,471 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-#ifdef HAVE_JACK
-
-#include "AudioJACKTarget.h"
-#include "AudioCallbackPlaySource.h"
-
-#include <iostream>
-#include <cmath>
-
-#include <alloca.h>
-
-//#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) {
-                cerr << "WARNING: AudioJACKTarget: Failed to load JACK library: "
-                          << ::dlerror() << " (tried .so, .so.0, .so.1)"
-                          << endl;
-            }
-            attempted = true;
-        }
-        if (!library) return 0;
-    }
-    void *symbol = ::dlsym(library, name);
-    if (!symbol) {
-        cerr << "WARNING: AudioJACKTarget: Failed to locate symbol "
-                  << name << ": " << ::dlerror() << endl;
-    }
-    symbols[name] = symbol;
-    return symbol;
-}
-
-static jack_client_t *dynamic_jack_client_open(const char *client_name,
-                                               jack_options_t options,
-                                               jack_status_t *status, ...)
-{
-    typedef jack_client_t *(*func)(const char *client_name,
-                                   jack_options_t options,
-                                   jack_status_t *status, ...);
-    void *s = symbol("jack_client_open");
-    if (!s) return 0;
-    func f = (func)s;
-    return f(client_name, options, status); // varargs not supported here
-}
-
-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_frame_time, jack_client_t *, 0);
-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_client_open dynamic_jack_client_open
-#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_frame_time dynamic_jack_frame_time
-#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),
-    m_done(false)
-{
-    JackOptions options = JackNullOption;
-#ifdef HAVE_PORTAUDIO_2_0
-    options = JackNoStartServer;
-#endif
-#ifdef HAVE_LIBPULSE
-    options = JackNoStartServer;
-#endif
-
-    JackStatus status = JackStatus(0);
-    m_client = jack_client_open(source->getClientName().toLocal8Bit().data(),
-                                options, &status);
-    
-    if (!m_client) {
-        cerr << "AudioJACKTarget: Failed to connect to JACK server: status code "
-                  << status << endl;
-        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)) {
-	cerr << "ERROR: AudioJACKTarget: Failed to activate JACK client"
-		  << endl;
-    }
-
-    if (m_source) {
-	sourceModelReplaced();
-    }
-    
-    // Mainstream JACK (though not jackdmp) calls mlockall() to lock
-    // down all memory for real-time operation.  That isn't a terribly
-    // good idea in an application like this that may have very high
-    // dynamic memory usage in other threads, as mlockall() applies
-    // across all threads.  We're far better off undoing it here and
-    // accepting the possible loss of true RT capability.
-    MUNLOCKALL();
-}
-
-AudioJACKTarget::~AudioJACKTarget()
-{
-    SVDEBUG << "AudioJACKTarget::~AudioJACKTarget()" << endl;
-
-    if (m_source) {
-        m_source->setTarget(0, m_bufferSize);
-    }
-
-    shutdown();
-
-    if (m_client) {
-
-        while (m_outputs.size() > 0) {
-            std::vector<jack_port_t *>::iterator itr = m_outputs.end();
-            --itr;
-            jack_port_t *port = *itr;
-            cerr << "unregister " << m_outputs.size() << endl;
-            if (port) jack_port_unregister(m_client, port);
-            m_outputs.erase(itr);
-        }
-        cerr << "Deactivating... ";
-	jack_deactivate(m_client);
-        cerr << "done\nClosing... ";
-	jack_client_close(m_client);
-        cerr << "done" << endl;
-    }
-
-    m_client = 0;
-
-    SVDEBUG << "AudioJACKTarget::~AudioJACKTarget() done" << endl;
-}
-
-void
-AudioJACKTarget::shutdown()
-{
-    m_done = true;
-}
-
-bool
-AudioJACKTarget::isOK() const
-{
-    return (m_client != 0);
-}
-
-double
-AudioJACKTarget::getCurrentTime() const
-{
-    if (m_client && m_sampleRate) {
-        return double(jack_frame_time(m_client)) / double(m_sampleRate);
-    } else {
-        return 0.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->setTarget(this, 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    
-    SVDEBUG << "AudioJACKTarget::sourceModelReplaced: have " << channels << " channels and " << physicalPortCount << " physical ports" << endl;
-#endif
-
-    while (m_outputs.size() < channels) {
-	
-	char name[20];
-	jack_port_t *port;
-
-	sprintf(name, "out %d", int(m_outputs.size() + 1));
-
-	port = jack_port_register(m_client,
-				  name,
-				  JACK_DEFAULT_AUDIO_TYPE,
-				  JackPortIsOutput,
-				  0);
-
-	if (!port) {
-	    cerr
-		<< "ERROR: AudioJACKTarget: Failed to create JACK output port "
-		<< m_outputs.size() << 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_done) return 0;
-
-    if (!m_mutex.tryLock()) {
-	return 0;
-    }
-
-    if (m_outputs.empty()) {
-	m_mutex.unlock();
-	return 0;
-    }
-
-#ifdef DEBUG_AUDIO_JACK_TARGET    
-    cout << "AudioJACKTarget::process(" << nframes << "): have a source" << endl;
-#endif
-
-#ifdef DEBUG_AUDIO_JACK_TARGET    
-    if (m_bufferSize != nframes) {
-	cerr << "WARNING: m_bufferSize != nframes (" << m_bufferSize << " != " << nframes << ")" << 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()
-{
-    cerr << "AudioJACKTarget: xrun!" << endl;
-    if (m_source) m_source->audioProcessingOverload();
-    return 0;
-}
-
-#endif /* HAVE_JACK */
-
--- a/audioio/AudioJACKTarget.h	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +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 void shutdown();
-
-    virtual bool isOK() const;
-
-    virtual double getCurrentTime() 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;
-    bool                        m_done;
-};
-
-#endif /* HAVE_JACK */
-
-#endif
-
--- a/audioio/AudioPortAudioTarget.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,300 +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_2_0
-
-#include "AudioPortAudioTarget.h"
-#include "AudioCallbackPlaySource.h"
-
-#include <iostream>
-#include <cassert>
-#include <cmath>
-
-#ifndef _WIN32
-#include <pthread.h>
-#endif
-
-//#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),
-    m_prioritySet(false),
-    m_done(false)
-{
-    PaError err;
-
-#ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
-    cerr << "AudioPortAudioTarget: Initialising for PortAudio v19" << endl;
-#endif
-
-    err = Pa_Initialize();
-    if (err != paNoError) {
-	cerr << "ERROR: AudioPortAudioTarget: Failed to initialize PortAudio: " << Pa_GetErrorText(err) << endl;
-	return;
-    }
-
-    m_bufferSize = 2048;
-    m_sampleRate = 44100;
-    if (m_source && (m_source->getSourceSampleRate() != 0)) {
-	m_sampleRate = m_source->getSourceSampleRate();
-    }
-
-    PaStreamParameters op;
-    op.device = Pa_GetDefaultOutputDevice();
-    op.channelCount = 2;
-    op.sampleFormat = paFloat32;
-    op.suggestedLatency = 0.2;
-    op.hostApiSpecificStreamInfo = 0;
-    err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
-                        paFramesPerBufferUnspecified,
-                        paNoFlag, processStatic, this);
-
-    if (err != paNoError) {
-
-        cerr << "WARNING: AudioPortAudioTarget: Failed to open PortAudio stream with default frames per buffer, trying again with fixed frames per buffer..." << endl;
-        
-        err = Pa_OpenStream(&m_stream, 0, &op, m_sampleRate,
-                            1024,
-                            paNoFlag, processStatic, this);
-	m_bufferSize = 1024;
-    }
-
-    if (err != paNoError) {
-	cerr << "ERROR: AudioPortAudioTarget: Failed to open PortAudio stream: " << Pa_GetErrorText(err) << endl;
-        cerr << "Note: device ID was " << op.device << endl;
-	m_stream = 0;
-	Pa_Terminate();
-	return;
-    }
-
-    const PaStreamInfo *info = Pa_GetStreamInfo(m_stream);
-    m_latency = int(info->outputLatency * m_sampleRate + 0.001);
-    if (m_bufferSize < m_latency) m_bufferSize = m_latency;
-
-    cerr << "PortAudio latency = " << m_latency << " frames" << endl;
-
-    err = Pa_StartStream(m_stream);
-
-    if (err != paNoError) {
-	cerr << "ERROR: AudioPortAudioTarget: Failed to start PortAudio stream: " << Pa_GetErrorText(err) << endl;
-	Pa_CloseStream(m_stream);
-	m_stream = 0;
-	Pa_Terminate();
-	return;
-    }
-
-    if (m_source) {
-	cerr << "AudioPortAudioTarget: block size " << m_bufferSize << endl;
-	m_source->setTarget(this, m_bufferSize);
-	m_source->setTargetSampleRate(m_sampleRate);
-	m_source->setTargetPlayLatency(m_latency);
-    }
-
-#ifdef DEBUG_PORT_AUDIO_TARGET
-    cerr << "AudioPortAudioTarget: initialised OK" << endl;
-#endif
-}
-
-AudioPortAudioTarget::~AudioPortAudioTarget()
-{
-    SVDEBUG << "AudioPortAudioTarget::~AudioPortAudioTarget()" << endl;
-
-    if (m_source) {
-        m_source->setTarget(0, m_bufferSize);
-    }
-
-    shutdown();
-
-    if (m_stream) {
-
-        SVDEBUG << "closing stream" << endl;
-
-	PaError err;
-	err = Pa_CloseStream(m_stream);
-	if (err != paNoError) {
-	    cerr << "ERROR: AudioPortAudioTarget: Failed to close PortAudio stream: " << Pa_GetErrorText(err) << endl;
-	}
-
-        cerr << "terminating" << endl;
-
-	err = Pa_Terminate();
-        if (err != paNoError) {
-            cerr << "ERROR: AudioPortAudioTarget: Failed to terminate PortAudio: " << Pa_GetErrorText(err) << endl;
-	}   
-    }
-
-    m_stream = 0;
-
-    SVDEBUG << "AudioPortAudioTarget::~AudioPortAudioTarget() done" << endl;
-}
-
-void 
-AudioPortAudioTarget::shutdown()
-{
-#ifdef DEBUG_PORT_AUDIO_TARGET
-    SVDEBUG << "AudioPortAudioTarget::shutdown" << endl;
-#endif
-    m_done = true;
-}
-
-bool
-AudioPortAudioTarget::isOK() const
-{
-    return (m_stream != 0);
-}
-
-double
-AudioPortAudioTarget::getCurrentTime() const
-{
-    if (!m_stream) return 0.0;
-    else return Pa_GetStreamTime(m_stream);
-}
-
-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);
-}
-
-void
-AudioPortAudioTarget::sourceModelReplaced()
-{
-    m_source->setTargetSampleRate(m_sampleRate);
-}
-
-int
-AudioPortAudioTarget::process(const void *, void *outputBuffer,
-                              unsigned long nframes,
-                              const PaStreamCallbackTimeInfo *,
-                              PaStreamCallbackFlags)
-{
-#ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET    
-    SVDEBUG << "AudioPortAudioTarget::process(" << nframes << ")" << endl;
-#endif
-
-    if (!m_source || m_done) {
-#ifdef DEBUG_AUDIO_PORT_AUDIO_TARGET
-        SVDEBUG << "AudioPortAudioTarget::process: Doing nothing, no source or application done" << endl;
-#endif
-        return 0;
-    }
-
-    if (!m_prioritySet) {
-#ifndef _WIN32
-        sched_param param;
-        param.sched_priority = 20;
-        if (pthread_setschedparam(pthread_self(), SCHED_RR, &param)) {
-            SVDEBUG << "AudioPortAudioTarget: NOTE: couldn't set RT scheduling class" << endl;
-        } else {
-            SVDEBUG << "AudioPortAudioTarget: NOTE: successfully set RT scheduling class" << endl;
-        }
-#endif
-        m_prioritySet = true;
-    }
-
-    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);
-
-    if (Pa_GetStreamCpuLoad(m_stream) > 0.7) {
-        if (m_source) m_source->audioProcessingOverload();
-    }
-
-    return 0;
-}
-
-#endif /* HAVE_PORTAUDIO */
-
--- a/audioio/AudioPortAudioTarget.h	Tue Jul 14 15:04:45 2015 +0100
+++ /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.
-*/
-
-#ifndef _AUDIO_PORT_AUDIO_TARGET_H_
-#define _AUDIO_PORT_AUDIO_TARGET_H_
-
-#ifdef HAVE_PORTAUDIO_2_0
-
-// This code requires PortAudio v19 -- it won't work with v18.
-
-#include <portaudio.h>
-
-#include <QObject>
-
-#include "AudioCallbackPlayTarget.h"
-
-class AudioCallbackPlaySource;
-
-class AudioPortAudioTarget : public AudioCallbackPlayTarget
-{
-    Q_OBJECT
-
-public:
-    AudioPortAudioTarget(AudioCallbackPlaySource *source);
-    virtual ~AudioPortAudioTarget();
-
-    virtual void shutdown();
-
-    virtual bool isOK() const;
-
-    virtual double getCurrentTime() const;
-
-public slots:
-    virtual void sourceModelReplaced();
-
-protected:
-    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;
-
-    int m_bufferSize;
-    int m_sampleRate;
-    int m_latency;
-    bool m_prioritySet;
-    bool m_done;
-};
-
-#endif /* HAVE_PORTAUDIO */
-
-#endif
-
--- a/audioio/AudioPulseAudioTarget.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,433 +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 2008 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.
-*/
-
-#ifdef HAVE_LIBPULSE
-
-#include "AudioPulseAudioTarget.h"
-#include "AudioCallbackPlaySource.h"
-
-#include <QMutexLocker>
-
-#include <iostream>
-#include <cassert>
-#include <cmath>
-
-#define DEBUG_AUDIO_PULSE_AUDIO_TARGET 1
-//#define DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY 1
-
-AudioPulseAudioTarget::AudioPulseAudioTarget(AudioCallbackPlaySource *source) :
-    AudioCallbackPlayTarget(source),
-    m_mutex(QMutex::Recursive),
-    m_loop(0),
-    m_api(0),
-    m_context(0),
-    m_stream(0),
-    m_loopThread(0),
-    m_bufferSize(0),
-    m_sampleRate(0),
-    m_latency(0),
-    m_done(false)
-{
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
-    cerr << "AudioPulseAudioTarget: Initialising for PulseAudio" << endl;
-#endif
-
-    m_loop = pa_mainloop_new();
-    if (!m_loop) {
-        cerr << "ERROR: AudioPulseAudioTarget: Failed to create main loop" << endl;
-        return;
-    }
-
-    m_api = pa_mainloop_get_api(m_loop);
-
-    //!!! handle signals how?
-
-    m_bufferSize = 20480;
-    m_sampleRate = 44100;
-    if (m_source && (m_source->getSourceSampleRate() != 0)) {
-	m_sampleRate = m_source->getSourceSampleRate();
-    }
-    m_spec.rate = m_sampleRate;
-    m_spec.channels = 2;
-    m_spec.format = PA_SAMPLE_FLOAT32NE;
-
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
-    cerr << "AudioPulseAudioTarget: Creating context" << endl;
-#endif
-
-    m_context = pa_context_new(m_api, source->getClientName().toLocal8Bit().data());
-    if (!m_context) {
-        cerr << "ERROR: AudioPulseAudioTarget: Failed to create context object" << endl;
-        return;
-    }
-
-    pa_context_set_state_callback(m_context, contextStateChangedStatic, this);
-
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
-    cerr << "AudioPulseAudioTarget: Connecting to default server..." << endl;
-#endif
-
-    pa_context_connect(m_context, 0, // default server
-                       (pa_context_flags_t)PA_CONTEXT_NOAUTOSPAWN, 0);
-
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
-    cerr << "AudioPulseAudioTarget: Starting main loop" << endl;
-#endif
-
-    m_loopThread = new MainLoopThread(m_loop);
-    m_loopThread->start();
-
-#ifdef DEBUG_PULSE_AUDIO_TARGET
-    cerr << "AudioPulseAudioTarget: initialised OK" << endl;
-#endif
-}
-
-AudioPulseAudioTarget::~AudioPulseAudioTarget()
-{
-    SVDEBUG << "AudioPulseAudioTarget::~AudioPulseAudioTarget()" << endl;
-
-    if (m_source) {
-        m_source->setTarget(0, m_bufferSize);
-    }
-
-    shutdown();
-
-    QMutexLocker locker(&m_mutex);
-
-    if (m_stream) pa_stream_unref(m_stream);
-
-    if (m_context) pa_context_unref(m_context);
-
-    if (m_loop) {
-        pa_signal_done();
-        pa_mainloop_free(m_loop);
-    }
-
-    m_stream = 0;
-    m_context = 0;
-    m_loop = 0;
-
-    SVDEBUG << "AudioPulseAudioTarget::~AudioPulseAudioTarget() done" << endl;
-}
-
-void 
-AudioPulseAudioTarget::shutdown()
-{
-    m_done = true;
-}
-
-bool
-AudioPulseAudioTarget::isOK() const
-{
-    return (m_context != 0);
-}
-
-double
-AudioPulseAudioTarget::getCurrentTime() const
-{
-    if (!m_stream) return 0.0;
-    
-    pa_usec_t usec = 0;
-    pa_stream_get_time(m_stream, &usec);
-    return usec / 1000000.f;
-}
-
-void
-AudioPulseAudioTarget::sourceModelReplaced()
-{
-    m_source->setTargetSampleRate(m_sampleRate);
-}
-
-void
-AudioPulseAudioTarget::streamWriteStatic(pa_stream *stream,
-                                         size_t length,
-                                         void *data)
-{
-    AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
-    
-    assert(stream == target->m_stream);
-
-    target->streamWrite(length);
-}
-
-void
-AudioPulseAudioTarget::streamWrite(size_t requested)
-{
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
-    cout << "AudioPulseAudioTarget::streamWrite(" << requested << ")" << endl;
-#endif
-    if (m_done) return;
-
-    QMutexLocker locker(&m_mutex);
-
-    pa_usec_t latency = 0;
-    int negative = 0;
-    if (!pa_stream_get_latency(m_stream, &latency, &negative)) {
-        int latframes = (latency / 1000000.f) * float(m_sampleRate);
-        if (latframes > 0) m_source->setTargetPlayLatency(latframes);
-    }
-
-    static float *output = 0;
-    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;
-
-    size_t nframes = requested / (sourceChannels * sizeof(float));
-
-    if (nframes > m_bufferSize) {
-        cerr << "WARNING: AudioPulseAudioTarget::streamWrite: nframes " << nframes << " > m_bufferSize " << m_bufferSize << endl;
-    }
-
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
-    cout << "AudioPulseAudioTarget::streamWrite: nframes = " << nframes << endl;
-#endif
-
-    if (!tmpbuf || tmpbufch != sourceChannels || int(tmpbufsz) < nframes) {
-
-	if (tmpbuf) {
-	    for (size_t i = 0; i < tmpbufch; ++i) {
-		delete[] tmpbuf[i];
-	    }
-	    delete[] tmpbuf;
-	}
-
-        if (output) {
-            delete[] output;
-        }
-
-	tmpbufch = sourceChannels;
-	tmpbufsz = nframes;
-	tmpbuf = new float *[tmpbufch];
-
-	for (size_t i = 0; i < tmpbufch; ++i) {
-	    tmpbuf[i] = new float[tmpbufsz];
-	}
-
-        output = new float[tmpbufsz * tmpbufch];
-    }
-	
-    size_t received = m_source->getSourceSamples(nframes, tmpbuf);
-
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
-    cerr << "requested " << nframes << ", received " << received << endl;
-
-    if (received < nframes) {
-        cerr << "*** WARNING: Wrong number of frames received" << endl;
-    }
-#endif
-
-    float peakLeft = 0.0, peakRight = 0.0;
-
-    for (size_t ch = 0; ch < 2; ++ch) {
-	
-	float peak = 0.0;
-
-	if (ch < sourceChannels) {
-
-	    // PulseAudio 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;
-    }
-
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET_PLAY
-    SVDEBUG << "calling pa_stream_write with "
-              << nframes * tmpbufch * sizeof(float) << " bytes" << endl;
-#endif
-
-    pa_stream_write(m_stream, output, nframes * tmpbufch * sizeof(float),
-                    0, 0, PA_SEEK_RELATIVE);
-
-    m_source->setOutputLevels(peakLeft, peakRight);
-
-    return;
-}
-
-void
-AudioPulseAudioTarget::streamStateChangedStatic(pa_stream *stream,
-                                                void *data)
-{
-    AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
-    
-    assert(stream == target->m_stream);
-
-    target->streamStateChanged();
-}
-
-void
-AudioPulseAudioTarget::streamStateChanged()
-{
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
-    SVDEBUG << "AudioPulseAudioTarget::streamStateChanged" << endl;
-#endif
-    QMutexLocker locker(&m_mutex);
-
-    switch (pa_stream_get_state(m_stream)) {
-
-        case PA_STREAM_CREATING:
-        case PA_STREAM_TERMINATED:
-            break;
-
-        case PA_STREAM_READY:
-        {
-            SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: Ready" << endl;
-
-            pa_usec_t latency = 0;
-            int negative = 0;
-            if (pa_stream_get_latency(m_stream, &latency, &negative)) {
-                cerr << "AudioPulseAudioTarget::streamStateChanged: Failed to query latency" << endl;
-            }
-            cerr << "Latency = " << latency << " usec" << endl;
-            int latframes = (latency / 1000000.f) * float(m_sampleRate);
-            cerr << "that's " << latframes << " frames" << endl;
-
-            const pa_buffer_attr *attr;
-            if (!(attr = pa_stream_get_buffer_attr(m_stream))) {
-                SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: Cannot query stream buffer attributes" << endl;
-                m_source->setTarget(this, m_bufferSize);
-                m_source->setTargetSampleRate(m_sampleRate);
-                if (latframes != 0) m_source->setTargetPlayLatency(latframes);
-            } else {
-                int targetLength = attr->tlength;
-                SVDEBUG << "AudioPulseAudioTarget::streamStateChanged: stream target length = " << targetLength << endl;
-                m_source->setTarget(this, targetLength);
-                m_source->setTargetSampleRate(m_sampleRate);
-                if (latframes == 0) latframes = targetLength;
-                cerr << "latency = " << latframes << endl;
-                m_source->setTargetPlayLatency(latframes);
-            }
-        }
-            break;
-
-        case PA_STREAM_FAILED:
-        default:
-            cerr << "AudioPulseAudioTarget::streamStateChanged: Error: "
-                      << pa_strerror(pa_context_errno(m_context)) << endl;
-            //!!! do something...
-            break;
-    }
-}
-
-void
-AudioPulseAudioTarget::contextStateChangedStatic(pa_context *context,
-                                                 void *data)
-{
-    AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
-    
-    assert(context == target->m_context);
-
-    target->contextStateChanged();
-}
-
-void
-AudioPulseAudioTarget::contextStateChanged()
-{
-#ifdef DEBUG_AUDIO_PULSE_AUDIO_TARGET
-    SVDEBUG << "AudioPulseAudioTarget::contextStateChanged" << endl;
-#endif
-    QMutexLocker locker(&m_mutex);
-
-    switch (pa_context_get_state(m_context)) {
-
-        case PA_CONTEXT_CONNECTING:
-        case PA_CONTEXT_AUTHORIZING:
-        case PA_CONTEXT_SETTING_NAME:
-            break;
-
-        case PA_CONTEXT_READY:
-            SVDEBUG << "AudioPulseAudioTarget::contextStateChanged: Ready"
-                      << endl;
-
-            m_stream = pa_stream_new(m_context, "stream", &m_spec, 0);
-            assert(m_stream); //!!!
-            
-            pa_stream_set_state_callback(m_stream, streamStateChangedStatic, this);
-            pa_stream_set_write_callback(m_stream, streamWriteStatic, this);
-            pa_stream_set_overflow_callback(m_stream, streamOverflowStatic, this);
-            pa_stream_set_underflow_callback(m_stream, streamUnderflowStatic, this);
-            if (pa_stream_connect_playback
-                (m_stream, 0, 0,
-                 pa_stream_flags_t(PA_STREAM_INTERPOLATE_TIMING |
-                                   PA_STREAM_AUTO_TIMING_UPDATE),
-                 0, 0)) { //??? return value
-                cerr << "AudioPulseAudioTarget: Failed to connect playback stream" << endl;
-            }
-
-            break;
-
-        case PA_CONTEXT_TERMINATED:
-            SVDEBUG << "AudioPulseAudioTarget::contextStateChanged: Terminated" << endl;
-            //!!! do something...
-            break;
-
-        case PA_CONTEXT_FAILED:
-        default:
-            cerr << "AudioPulseAudioTarget::contextStateChanged: Error: "
-                      << pa_strerror(pa_context_errno(m_context)) << endl;
-            //!!! do something...
-            break;
-    }
-}
-
-void
-AudioPulseAudioTarget::streamOverflowStatic(pa_stream *, void *)
-{
-    SVDEBUG << "AudioPulseAudioTarget::streamOverflowStatic: Overflow!" << endl;
-}
-
-void
-AudioPulseAudioTarget::streamUnderflowStatic(pa_stream *, void *data)
-{
-    SVDEBUG << "AudioPulseAudioTarget::streamUnderflowStatic: Underflow!" << endl;
-    AudioPulseAudioTarget *target = (AudioPulseAudioTarget *)data;
-    if (target && target->m_source) {
-        target->m_source->audioProcessingOverload();
-    }
-}
-
-#endif /* HAVE_PULSEAUDIO */
-
--- a/audioio/AudioPulseAudioTarget.h	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +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 2008 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_PULSE_AUDIO_TARGET_H_
-#define _AUDIO_PULSE_AUDIO_TARGET_H_
-
-#ifdef HAVE_LIBPULSE
-
-#include <pulse/pulseaudio.h>
-
-#include <QObject>
-#include <QMutex>
-#include "base/Thread.h"
-
-#include "AudioCallbackPlayTarget.h"
-
-class AudioCallbackPlaySource;
-
-class AudioPulseAudioTarget : public AudioCallbackPlayTarget
-{
-    Q_OBJECT
-
-public:
-    AudioPulseAudioTarget(AudioCallbackPlaySource *source);
-    virtual ~AudioPulseAudioTarget();
-
-    virtual void shutdown();
-
-    virtual bool isOK() const;
-
-    virtual double getCurrentTime() const;
-
-public slots:
-    virtual void sourceModelReplaced();
-
-protected:
-    void streamWrite(size_t);
-    void streamStateChanged();
-    void contextStateChanged();
-
-    static void streamWriteStatic(pa_stream *, size_t, void *);
-    static void streamStateChangedStatic(pa_stream *, void *);
-    static void streamOverflowStatic(pa_stream *, void *);
-    static void streamUnderflowStatic(pa_stream *, void *);
-    static void contextStateChangedStatic(pa_context *, void *);
-
-    QMutex m_mutex;
-
-    class MainLoopThread : public Thread
-    {
-    public:
-        MainLoopThread(pa_mainloop *loop) : Thread(NonRTThread), m_loop(loop) { } //!!! or RTThread
-        virtual void run() {
-            int rv = 0;
-            pa_mainloop_run(m_loop, &rv); //!!! check return value from this, and rv
-        }
-
-    private:
-        pa_mainloop *m_loop;
-    };
-
-    pa_mainloop *m_loop;
-    pa_mainloop_api *m_api;
-    pa_context *m_context;
-    pa_stream *m_stream;
-    pa_sample_spec m_spec;
-
-    MainLoopThread *m_loopThread;
-
-    int m_bufferSize;
-    int m_sampleRate;
-    int m_latency;
-    bool m_done;
-};
-
-#endif /* HAVE_PULSEAUDIO */
-
-#endif
-
--- a/audioio/AudioTargetFactory.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,186 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 Chris Cannam.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-#include "AudioTargetFactory.h"
-
-#include "AudioJACKTarget.h"
-#include "AudioCoreAudioTarget.h"
-#include "AudioPortAudioTarget.h"
-#include "AudioPulseAudioTarget.h"
-
-#include "AudioCallbackPlayTarget.h"
-
-#include <QCoreApplication>
-
-#include <iostream>
-
-AudioTargetFactory *
-AudioTargetFactory::m_instance = 0;
-
-AudioTargetFactory *
-AudioTargetFactory::getInstance()
-{
-    if (!m_instance) m_instance = new AudioTargetFactory();
-    return m_instance;
-}
-
-AudioTargetFactory::AudioTargetFactory()
-{
-}
-
-std::vector<QString>
-AudioTargetFactory::getCallbackTargetNames(bool includeAuto) const
-{
-    std::vector<QString> names;
-    if (includeAuto) names.push_back("auto");
-
-#ifdef HAVE_JACK
-    names.push_back("jack");
-#endif
-
-#ifdef HAVE_LIBPULSE
-    names.push_back("pulse");
-#endif
-
-#ifdef HAVE_COREAUDIO
-    names.push_back("core");
-#endif
-
-#ifdef HAVE_PORTAUDIO_2_0
-    names.push_back("port");
-#endif
-
-    return names;
-}
-
-QString
-AudioTargetFactory::getCallbackTargetDescription(QString name) const
-{
-    if (name == "auto") {
-        return QCoreApplication::translate("AudioTargetFactory",
-                                           "(auto)");
-    }
-    if (name == "jack") {
-        return QCoreApplication::translate("AudioTargetFactory",
-                                           "JACK Audio Connection Kit");
-    }
-    if (name == "pulse") {
-        return QCoreApplication::translate("AudioTargetFactory",
-                                           "PulseAudio Server");
-    }
-    if (name == "core") {
-        return QCoreApplication::translate("AudioTargetFactory",
-                                           "Core Audio Device");
-    }
-    if (name == "port") {
-        return QCoreApplication::translate("AudioTargetFactory",
-                                           "Default Soundcard Device");
-    }
-
-    return "(unknown)";
-}
-
-QString
-AudioTargetFactory::getDefaultCallbackTarget() const
-{
-    if (m_default == "") return "auto";
-    return m_default;
-}
-
-bool
-AudioTargetFactory::isAutoCallbackTarget(QString name) const
-{
-    return (name == "auto" || name == "");
-}
-
-void
-AudioTargetFactory::setDefaultCallbackTarget(QString target)
-{
-    m_default = target;
-}
-
-AudioCallbackPlayTarget *
-AudioTargetFactory::createCallbackTarget(AudioCallbackPlaySource *source)
-{
-    AudioCallbackPlayTarget *target = 0;
-
-    if (m_default != "" && m_default != "auto") {
-
-#ifdef HAVE_JACK
-        if (m_default == "jack") target = new AudioJACKTarget(source);
-#endif
-
-#ifdef HAVE_LIBPULSE
-        if (m_default == "pulse") target = new AudioPulseAudioTarget(source);
-#endif
-
-#ifdef HAVE_COREAUDIO
-        if (m_default == "core") target = new AudioCoreAudioTarget(source);
-#endif
-
-#ifdef HAVE_PORTAUDIO_2_0
-        if (m_default == "port") target = new AudioPortAudioTarget(source);
-#endif
-
-        if (!target || !target->isOK()) {
-            cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open the requested target (\"" << m_default << "\")" << endl;
-            delete target;
-            return 0;
-        } else {
-            return target;
-        }
-    }
-
-#ifdef HAVE_JACK
-    target = new AudioJACKTarget(source);
-    if (target->isOK()) return target;
-    else {
-	cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open JACK target" << endl;
-	delete target;
-    }
-#endif
-    
-#ifdef HAVE_LIBPULSE
-    target = new AudioPulseAudioTarget(source);
-    if (target->isOK()) return target;
-    else {
-	cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open PulseAudio target" << endl;
-	delete target;
-    }
-#endif
-
-#ifdef HAVE_COREAUDIO
-    target = new AudioCoreAudioTarget(source);
-    if (target->isOK()) return target;
-    else {
-	cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open CoreAudio target" << endl;
-	delete target;
-    }
-#endif
-    
-#ifdef HAVE_PORTAUDIO_2_0
-    target = new AudioPortAudioTarget(source);
-    if (target->isOK()) return target;
-    else {
-	cerr << "WARNING: AudioTargetFactory::createCallbackTarget: Failed to open PortAudio target" << endl;
-	delete target;
-    }
-#endif
-
-    cerr << "WARNING: AudioTargetFactory::createCallbackTarget: No suitable targets available" << endl;
-    return 0;
-}
-
-
--- a/audioio/AudioTargetFactory.h	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,47 +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_
-
-#include <vector>
-#include <QString>
-
-#include "base/Debug.h"
-
-class AudioCallbackPlaySource;
-class AudioCallbackPlayTarget;
-
-class AudioTargetFactory 
-{
-public:
-    static AudioTargetFactory *getInstance();
-
-    std::vector<QString> getCallbackTargetNames(bool includeAuto = true) const;
-    QString getCallbackTargetDescription(QString name) const;
-    QString getDefaultCallbackTarget() const;
-    bool isAutoCallbackTarget(QString name) const;
-    void setDefaultCallbackTarget(QString name);
-
-    AudioCallbackPlayTarget *createCallbackTarget(AudioCallbackPlaySource *);
-
-protected:
-    AudioTargetFactory();
-    static AudioTargetFactory *m_instance;
-    QString m_default;
-};
-
-#endif
-
--- a/audioio/PlaySpeedRangeMapper.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ /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;
-    }
-
-//    cerr << "value = " << value << " slow = " << slow << " factor = " << factor << " position = " << position << 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;
-//    cerr << "position = " << position << " percent = " << pc << 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));
-    }
-
-//    cerr << "value = " << value << " factor = " << factor << 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;
-
-//    cerr << "position = " << position << " slow = " << slow << " factor = " << factor << endl;
-
-    return factor;
-}
-
-QString
-PlaySpeedRangeMapper::getUnit() const
-{
-    return "%";
-}
--- a/audioio/PlaySpeedRangeMapper.h	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,43 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    This file copyright 2006 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/audioio/audioio.pro	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,33 +0,0 @@
-TEMPLATE = lib
-
-SV_UNIT_PACKAGES = fftw3f samplerate jack portaudio-2.0 libpulse rubberband
-load(../prf/sv.prf)
-
-CONFIG += sv staticlib qt thread warn_on stl rtti exceptions
-QT -= gui
-
-TARGET = svaudioio
-
-DEPENDPATH += ..
-INCLUDEPATH += . ..
-OBJECTS_DIR = tmp_obj
-MOC_DIR = tmp_moc
-
-HEADERS += AudioCallbackPlaySource.h \
-           AudioCallbackPlayTarget.h \
-           AudioCoreAudioTarget.h \
-           AudioGenerator.h \
-           AudioJACKTarget.h \
-           AudioPortAudioTarget.h \
-           AudioPulseAudioTarget.h \
-           AudioTargetFactory.h \
-           PlaySpeedRangeMapper.h
-SOURCES += AudioCallbackPlaySource.cpp \
-           AudioCallbackPlayTarget.cpp \
-           AudioCoreAudioTarget.cpp \
-           AudioGenerator.cpp \
-           AudioJACKTarget.cpp \
-           AudioPortAudioTarget.cpp \
-           AudioPulseAudioTarget.cpp \
-           AudioTargetFactory.cpp \
-           PlaySpeedRangeMapper.cpp
--- a/configure	Tue Jul 14 15:04:45 2015 +0100
+++ b/configure	Wed Apr 20 12:06:28 2016 +0100
@@ -646,16 +646,12 @@
 libpulse_CFLAGS
 JACK_LIBS
 JACK_CFLAGS
-portaudio_2_0_LIBS
-portaudio_2_0_CFLAGS
+portaudio_LIBS
+portaudio_CFLAGS
 liblo_LIBS
 liblo_CFLAGS
 rubberband_LIBS
 rubberband_CFLAGS
-vamphostsdk_LIBS
-vamphostsdk_CFLAGS
-vamp_LIBS
-vamp_CFLAGS
 samplerate_LIBS
 samplerate_CFLAGS
 sndfile_LIBS
@@ -673,6 +669,7 @@
 EGREP
 GREP
 CXXCPP
+HAVE_CXX11
 MKDIR_P
 INSTALL_DATA
 INSTALL_SCRIPT
@@ -755,16 +752,12 @@
 sndfile_LIBS
 samplerate_CFLAGS
 samplerate_LIBS
-vamp_CFLAGS
-vamp_LIBS
-vamphostsdk_CFLAGS
-vamphostsdk_LIBS
 rubberband_CFLAGS
 rubberband_LIBS
 liblo_CFLAGS
 liblo_LIBS
-portaudio_2_0_CFLAGS
-portaudio_2_0_LIBS
+portaudio_CFLAGS
+portaudio_LIBS
 JACK_CFLAGS
 JACK_LIBS
 libpulse_CFLAGS
@@ -1422,12 +1415,6 @@
               C compiler flags for samplerate, overriding pkg-config
   samplerate_LIBS
               linker flags for samplerate, overriding pkg-config
-  vamp_CFLAGS C compiler flags for vamp, overriding pkg-config
-  vamp_LIBS   linker flags for vamp, overriding pkg-config
-  vamphostsdk_CFLAGS
-              C compiler flags for vamphostsdk, overriding pkg-config
-  vamphostsdk_LIBS
-              linker flags for vamphostsdk, overriding pkg-config
   rubberband_CFLAGS
               C compiler flags for rubberband, overriding pkg-config
   rubberband_LIBS
@@ -1435,10 +1422,10 @@
   liblo_CFLAGS
               C compiler flags for liblo, overriding pkg-config
   liblo_LIBS  linker flags for liblo, overriding pkg-config
-  portaudio_2_0_CFLAGS
-              C compiler flags for portaudio_2_0, overriding pkg-config
-  portaudio_2_0_LIBS
-              linker flags for portaudio_2_0, overriding pkg-config
+  portaudio_CFLAGS
+              C compiler flags for portaudio, overriding pkg-config
+  portaudio_LIBS
+              linker flags for portaudio, overriding pkg-config
   JACK_CFLAGS C compiler flags for JACK, overriding pkg-config
   JACK_LIBS   linker flags for JACK, overriding pkg-config
   libpulse_CFLAGS
@@ -3449,6 +3436,146 @@
 $as_echo "$MKDIR_P" >&6; }
 
 
+# We are daringly making use of C++11 now
+
+    ax_cxx_compile_cxx11_required=true
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+  ac_success=no
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5
+$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; }
+if ${ax_cv_cxx_compile_cxx11+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    struct Base {
+    virtual void f() {}
+    };
+    struct Child : public Base {
+    virtual void f() override {}
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);
+
+    auto d = a;
+    auto l = [](){};
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  ax_cv_cxx_compile_cxx11=yes
+else
+  ax_cv_cxx_compile_cxx11=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5
+$as_echo "$ax_cv_cxx_compile_cxx11" >&6; }
+  if test x$ax_cv_cxx_compile_cxx11 = xyes; then
+    ac_success=yes
+  fi
+
+
+
+    if test x$ac_success = xno; then
+    for switch in -std=c++11 -std=c++0x; do
+      cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh`
+      { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5
+$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; }
+if eval \${$cachevar+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  ac_save_CXXFLAGS="$CXXFLAGS"
+         CXXFLAGS="$CXXFLAGS $switch"
+         cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  template <typename T>
+    struct check
+    {
+      static_assert(sizeof(int) <= sizeof(T), "not big enough");
+    };
+
+    struct Base {
+    virtual void f() {}
+    };
+    struct Child : public Base {
+    virtual void f() override {}
+    };
+
+    typedef check<check<bool>> right_angle_brackets;
+
+    int a;
+    decltype(a) b;
+
+    typedef check<int> check_type;
+    check_type c;
+    check_type&& cr = static_cast<check_type&&>(c);
+
+    auto d = a;
+    auto l = [](){};
+
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"; then :
+  eval $cachevar=yes
+else
+  eval $cachevar=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+         CXXFLAGS="$ac_save_CXXFLAGS"
+fi
+eval ac_res=\$$cachevar
+	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+      if eval test x\$$cachevar = xyes; then
+        CXXFLAGS="$CXXFLAGS $switch"
+        ac_success=yes
+        break
+      fi
+    done
+  fi
+  ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+  if test x$ax_cxx_compile_cxx11_required = xtrue; then
+    if test x$ac_success = xno; then
+      as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5
+    fi
+  else
+    if test x$ac_success = xno; then
+      HAVE_CXX11=0
+      { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5
+$as_echo "$as_me: No compiler with C++11 support was found" >&6;}
+    else
+      HAVE_CXX11=1
+
+$as_echo "#define HAVE_CXX11 1" >>confdefs.h
+
+    fi
+
+
+  fi
+
 
 ac_ext=cpp
 ac_cpp='$CXXCPP $CPPFLAGS'
@@ -3996,6 +4123,45 @@
 
 fi
 if test x$QMAKE = x ; then
+   	# Extract the first word of "qt5-qmake", so it can be a program name with args.
+set dummy qt5-qmake; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_QMAKE+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$QMAKE"; then
+  ac_cv_prog_QMAKE="$QMAKE" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $QTDIR/bin/
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_QMAKE="$QTDIR/bin/qt5-qmake"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+QMAKE=$ac_cv_prog_QMAKE
+if test -n "$QMAKE"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QMAKE" >&5
+$as_echo "$QMAKE" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test x$QMAKE = x ; then
    	# Extract the first word of "qmake", so it can be a program name with args.
 set dummy qmake; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -4113,6 +4279,45 @@
 
 fi
 if test x$QMAKE = x ; then
+   	# Extract the first word of "qt5-qmake", so it can be a program name with args.
+set dummy qt5-qmake; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_prog_QMAKE+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  if test -n "$QMAKE"; then
+  ac_cv_prog_QMAKE="$QMAKE" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_QMAKE="qt5-qmake"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+fi
+fi
+QMAKE=$ac_cv_prog_QMAKE
+if test -n "$QMAKE"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $QMAKE" >&5
+$as_echo "$QMAKE" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+fi
+if test x$QMAKE = x ; then
    	# Extract the first word of "qmake", so it can be a program name with args.
 set dummy qmake; ac_word=$2
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
@@ -4202,9 +4407,10 @@
 CXXFLAGS_MINIMAL="$AUTOCONF_CXXFLAGS"
 
 if test "x$GCC" = "xyes"; then
-        CXXFLAGS_DEBUG="-Wall -Woverloaded-virtual -Wextra -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -g -pipe"
-   	CXXFLAGS_RELEASE="-g0 -O2 -Wall -pipe"
-   	CXXFLAGS_MINIMAL="-g0 -O0"
+   	CXXFLAGS_ANY="-Wall -Wextra -Woverloaded-virtual -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -Wconversion -pipe"
+        CXXFLAGS_DEBUG="$CXXFLAGS_ANY -Werror -g"
+   	CXXFLAGS_RELEASE="$CXXFLAGS_ANY -g0 -O2"
+   	CXXFLAGS_MINIMAL="$CXXFLAGS_ANY -g0 -O0"
 fi
 
 CXXFLAGS_BUILD="$CXXFLAGS_RELEASE"
@@ -5006,18 +5212,18 @@
 fi
 
 
-SV_MODULE_MODULE=vamp
-SV_MODULE_VERSION_TEST="vamp >= 2.1"
-SV_MODULE_HEADER=vamp/vamp.h
-SV_MODULE_LIB=
-SV_MODULE_FUNC=
-SV_MODULE_HAVE=HAVE_$(echo vamp | tr 'a-z' 'A-Z')
+SV_MODULE_MODULE=rubberband
+SV_MODULE_VERSION_TEST="rubberband"
+SV_MODULE_HEADER=rubberband/RubberBandStretcher.h
+SV_MODULE_LIB=rubberband
+SV_MODULE_FUNC=rubberband_new
+SV_MODULE_HAVE=HAVE_$(echo rubberband | tr 'a-z' 'A-Z')
 SV_MODULE_FAILED=1
-if test -n "$vamp_LIBS" ; then
+if test -n "$rubberband_LIBS" ; then
    { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5
 $as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;}
-   CXXFLAGS="$CXXFLAGS $vamp_CFLAGS"
-   LIBS="$LIBS $vamp_LIBS"
+   CXXFLAGS="$CXXFLAGS $rubberband_CFLAGS"
+   LIBS="$LIBS $rubberband_LIBS"
    SV_MODULE_FAILED=""
 fi
 if test -z "$SV_MODULE_VERSION_TEST" ; then
@@ -5026,11 +5232,11 @@
 if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for vamp" >&5
-$as_echo_n "checking for vamp... " >&6; }
-
-if test -n "$vamp_CFLAGS"; then
-    pkg_cv_vamp_CFLAGS="$vamp_CFLAGS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rubberband" >&5
+$as_echo_n "checking for rubberband... " >&6; }
+
+if test -n "$rubberband_CFLAGS"; then
+    pkg_cv_rubberband_CFLAGS="$rubberband_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
     { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
@@ -5038,7 +5244,7 @@
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_vamp_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null`
+  pkg_cv_rubberband_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -5046,8 +5252,8 @@
  else
     pkg_failed=untried
 fi
-if test -n "$vamp_LIBS"; then
-    pkg_cv_vamp_LIBS="$vamp_LIBS"
+if test -n "$rubberband_LIBS"; then
+    pkg_cv_rubberband_LIBS="$rubberband_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
     { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
@@ -5055,7 +5261,7 @@
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_vamp_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null`
+  pkg_cv_rubberband_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -5076,12 +5282,12 @@
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        vamp_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
+	        rubberband_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
         else
-	        vamp_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
+	        rubberband_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
-	echo "$vamp_PKG_ERRORS" >&5
+	echo "$rubberband_PKG_ERRORS" >&5
 
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
 $as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
@@ -5091,11 +5297,11 @@
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
 $as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
 else
-	vamp_CFLAGS=$pkg_cv_vamp_CFLAGS
-	vamp_LIBS=$pkg_cv_vamp_LIBS
+	rubberband_CFLAGS=$pkg_cv_rubberband_CFLAGS
+	rubberband_LIBS=$pkg_cv_rubberband_LIBS
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $vamp_CFLAGS";LIBS="$LIBS $vamp_LIBS";SV_MODULE_FAILED=""
+	HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $rubberband_CFLAGS";LIBS="$LIBS $rubberband_LIBS";SV_MODULE_FAILED=""
 fi
 fi
 if test -n "$SV_MODULE_FAILED"; then
@@ -5157,18 +5363,19 @@
 fi
 
 
-SV_MODULE_MODULE=vamphostsdk
-SV_MODULE_VERSION_TEST="vamp-hostsdk >= 2.5"
-SV_MODULE_HEADER=vamp-hostsdk/PluginLoader.h
-SV_MODULE_LIB=vamp-hostsdk
-SV_MODULE_FUNC=libvamphostsdk_v_2_5_present
-SV_MODULE_HAVE=HAVE_$(echo vamphostsdk | tr 'a-z' 'A-Z')
+
+SV_MODULE_MODULE=liblo
+SV_MODULE_VERSION_TEST=""
+SV_MODULE_HEADER=lo/lo.h
+SV_MODULE_LIB=lo
+SV_MODULE_FUNC=lo_address_new
+SV_MODULE_HAVE=HAVE_$(echo liblo | tr 'a-z' 'A-Z')
 SV_MODULE_FAILED=1
-if test -n "$vamphostsdk_LIBS" ; then
+if test -n "$liblo_LIBS" ; then
    { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5
 $as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;}
-   CXXFLAGS="$CXXFLAGS $vamphostsdk_CFLAGS"
-   LIBS="$LIBS $vamphostsdk_LIBS"
+   CXXFLAGS="$CXXFLAGS $liblo_CFLAGS"
+   LIBS="$LIBS $liblo_LIBS"
    SV_MODULE_FAILED=""
 fi
 if test -z "$SV_MODULE_VERSION_TEST" ; then
@@ -5177,11 +5384,11 @@
 if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for vamphostsdk" >&5
-$as_echo_n "checking for vamphostsdk... " >&6; }
-
-if test -n "$vamphostsdk_CFLAGS"; then
-    pkg_cv_vamphostsdk_CFLAGS="$vamphostsdk_CFLAGS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for liblo" >&5
+$as_echo_n "checking for liblo... " >&6; }
+
+if test -n "$liblo_CFLAGS"; then
+    pkg_cv_liblo_CFLAGS="$liblo_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
     { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
@@ -5189,7 +5396,7 @@
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_vamphostsdk_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null`
+  pkg_cv_liblo_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -5197,8 +5404,8 @@
  else
     pkg_failed=untried
 fi
-if test -n "$vamphostsdk_LIBS"; then
-    pkg_cv_vamphostsdk_LIBS="$vamphostsdk_LIBS"
+if test -n "$liblo_LIBS"; then
+    pkg_cv_liblo_LIBS="$liblo_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
     { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
@@ -5206,7 +5413,7 @@
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_vamphostsdk_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null`
+  pkg_cv_liblo_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -5227,40 +5434,42 @@
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        vamphostsdk_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
+	        liblo_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
         else
-	        vamphostsdk_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
+	        liblo_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
-	echo "$vamphostsdk_PKG_ERRORS" >&5
-
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
-$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
+	echo "$liblo_PKG_ERRORS" >&5
+
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
+$as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
 elif test $pkg_failed = untried; then
      	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
-$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
-else
-	vamphostsdk_CFLAGS=$pkg_cv_vamphostsdk_CFLAGS
-	vamphostsdk_LIBS=$pkg_cv_vamphostsdk_LIBS
+	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
+$as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
+else
+	liblo_CFLAGS=$pkg_cv_liblo_CFLAGS
+	liblo_LIBS=$pkg_cv_liblo_LIBS
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $vamphostsdk_CFLAGS";LIBS="$LIBS $vamphostsdk_LIBS";SV_MODULE_FAILED=""
+	HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $liblo_CFLAGS";LIBS="$LIBS $liblo_LIBS";SV_MODULE_FAILED=""
 fi
 fi
 if test -n "$SV_MODULE_FAILED"; then
    as_ac_Header=`$as_echo "ac_cv_header_$SV_MODULE_HEADER" | $as_tr_sh`
 ac_fn_cxx_check_header_mongrel "$LINENO" "$SV_MODULE_HEADER" "$as_ac_Header" "$ac_includes_default"
 if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
-  HAVES="$HAVES $SV_MODULE_HAVE"
-else
-  as_fn_error $? "Failed to find header $SV_MODULE_HEADER for required module $SV_MODULE_MODULE" "$LINENO" 5
-fi
-
-
-   if test -n "$SV_MODULE_LIB"; then
-     as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh`
+  HAVES="$HAVES $SV_MODULE_HAVE";SV_MODULE_FAILED=""
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find header $SV_MODULE_HEADER for optional module $SV_MODULE_MODULE" >&5
+$as_echo "$as_me: Failed to find header $SV_MODULE_HEADER for optional module $SV_MODULE_MODULE" >&6;}
+fi
+
+
+   if test -z "$SV_MODULE_FAILED"; then
+      if test -n "$SV_MODULE_LIB"; then
+           as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh`
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB" >&5
 $as_echo_n "checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB... " >&6; }
 if eval \${$as_ac_Lib+:} false; then :
@@ -5301,25 +5510,27 @@
 if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
   LIBS="$LIBS -l$SV_MODULE_LIB"
 else
-  as_fn_error $? "Failed to find library $SV_MODULE_LIB for required module $SV_MODULE_MODULE" "$LINENO" 5
-fi
-
+  { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find library $SV_MODULE_LIB for optional module $SV_MODULE_MODULE" >&5
+$as_echo "$as_me: Failed to find library $SV_MODULE_LIB for optional module $SV_MODULE_MODULE" >&6;}
+fi
+
+      fi
    fi
 fi
 
 
-SV_MODULE_MODULE=rubberband
-SV_MODULE_VERSION_TEST="rubberband"
-SV_MODULE_HEADER=rubberband/RubberBandStretcher.h
-SV_MODULE_LIB=rubberband
-SV_MODULE_FUNC=rubberband_new
-SV_MODULE_HAVE=HAVE_$(echo rubberband | tr 'a-z' 'A-Z')
+SV_MODULE_MODULE=portaudio
+SV_MODULE_VERSION_TEST="portaudio-2.0 >= 19"
+SV_MODULE_HEADER=portaudio.h
+SV_MODULE_LIB=portaudio
+SV_MODULE_FUNC=Pa_IsFormatSupported
+SV_MODULE_HAVE=HAVE_$(echo portaudio | tr 'a-z' 'A-Z')
 SV_MODULE_FAILED=1
-if test -n "$rubberband_LIBS" ; then
+if test -n "$portaudio_LIBS" ; then
    { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5
 $as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;}
-   CXXFLAGS="$CXXFLAGS $rubberband_CFLAGS"
-   LIBS="$LIBS $rubberband_LIBS"
+   CXXFLAGS="$CXXFLAGS $portaudio_CFLAGS"
+   LIBS="$LIBS $portaudio_LIBS"
    SV_MODULE_FAILED=""
 fi
 if test -z "$SV_MODULE_VERSION_TEST" ; then
@@ -5328,11 +5539,11 @@
 if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for rubberband" >&5
-$as_echo_n "checking for rubberband... " >&6; }
-
-if test -n "$rubberband_CFLAGS"; then
-    pkg_cv_rubberband_CFLAGS="$rubberband_CFLAGS"
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for portaudio" >&5
+$as_echo_n "checking for portaudio... " >&6; }
+
+if test -n "$portaudio_CFLAGS"; then
+    pkg_cv_portaudio_CFLAGS="$portaudio_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
     { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
@@ -5340,7 +5551,7 @@
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_rubberband_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null`
+  pkg_cv_portaudio_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -5348,8 +5559,8 @@
  else
     pkg_failed=untried
 fi
-if test -n "$rubberband_LIBS"; then
-    pkg_cv_rubberband_LIBS="$rubberband_LIBS"
+if test -n "$portaudio_LIBS"; then
+    pkg_cv_portaudio_LIBS="$portaudio_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
     { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
@@ -5357,7 +5568,7 @@
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_rubberband_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null`
+  pkg_cv_portaudio_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null`
 		      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -5378,164 +5589,12 @@
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-	        rubberband_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
+	        portaudio_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
         else
-	        rubberband_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
+	        portaudio_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
         fi
 	# Put the nasty error message in config.log where it belongs
-	echo "$rubberband_PKG_ERRORS" >&5
-
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
-$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
-elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
-$as_echo "$as_me: Failed to find required module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
-else
-	rubberband_CFLAGS=$pkg_cv_rubberband_CFLAGS
-	rubberband_LIBS=$pkg_cv_rubberband_LIBS
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $rubberband_CFLAGS";LIBS="$LIBS $rubberband_LIBS";SV_MODULE_FAILED=""
-fi
-fi
-if test -n "$SV_MODULE_FAILED"; then
-   as_ac_Header=`$as_echo "ac_cv_header_$SV_MODULE_HEADER" | $as_tr_sh`
-ac_fn_cxx_check_header_mongrel "$LINENO" "$SV_MODULE_HEADER" "$as_ac_Header" "$ac_includes_default"
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
-  HAVES="$HAVES $SV_MODULE_HAVE"
-else
-  as_fn_error $? "Failed to find header $SV_MODULE_HEADER for required module $SV_MODULE_MODULE" "$LINENO" 5
-fi
-
-
-   if test -n "$SV_MODULE_LIB"; then
-     as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh`
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB" >&5
-$as_echo_n "checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB... " >&6; }
-if eval \${$as_ac_Lib+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-l$SV_MODULE_LIB  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char $SV_MODULE_FUNC ();
-int
-main ()
-{
-return $SV_MODULE_FUNC ();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_link "$LINENO"; then :
-  eval "$as_ac_Lib=yes"
-else
-  eval "$as_ac_Lib=no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-eval ac_res=\$$as_ac_Lib
-	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
-  LIBS="$LIBS -l$SV_MODULE_LIB"
-else
-  as_fn_error $? "Failed to find library $SV_MODULE_LIB for required module $SV_MODULE_MODULE" "$LINENO" 5
-fi
-
-   fi
-fi
-
-
-
-SV_MODULE_MODULE=liblo
-SV_MODULE_VERSION_TEST=""
-SV_MODULE_HEADER=lo/lo.h
-SV_MODULE_LIB=lo
-SV_MODULE_FUNC=lo_address_new
-SV_MODULE_HAVE=HAVE_$(echo liblo | tr 'a-z' 'A-Z')
-SV_MODULE_FAILED=1
-if test -n "$liblo_LIBS" ; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5
-$as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;}
-   CXXFLAGS="$CXXFLAGS $liblo_CFLAGS"
-   LIBS="$LIBS $liblo_LIBS"
-   SV_MODULE_FAILED=""
-fi
-if test -z "$SV_MODULE_VERSION_TEST" ; then
-   SV_MODULE_VERSION_TEST=$SV_MODULE_MODULE
-fi
-if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then
-
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for liblo" >&5
-$as_echo_n "checking for liblo... " >&6; }
-
-if test -n "$liblo_CFLAGS"; then
-    pkg_cv_liblo_CFLAGS="$liblo_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_liblo_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
-else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
-fi
-if test -n "$liblo_LIBS"; then
-    pkg_cv_liblo_LIBS="$liblo_LIBS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_liblo_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
-else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
-fi
-
-
-
-if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
-else
-        _pkg_short_errors_supported=no
-fi
-        if test $_pkg_short_errors_supported = yes; then
-	        liblo_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
-        else
-	        liblo_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$liblo_PKG_ERRORS" >&5
+	echo "$portaudio_PKG_ERRORS" >&5
 
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
 $as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
@@ -5545,166 +5604,11 @@
 	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
 $as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
 else
-	liblo_CFLAGS=$pkg_cv_liblo_CFLAGS
-	liblo_LIBS=$pkg_cv_liblo_LIBS
+	portaudio_CFLAGS=$pkg_cv_portaudio_CFLAGS
+	portaudio_LIBS=$pkg_cv_portaudio_LIBS
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
 $as_echo "yes" >&6; }
-	HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $liblo_CFLAGS";LIBS="$LIBS $liblo_LIBS";SV_MODULE_FAILED=""
-fi
-fi
-if test -n "$SV_MODULE_FAILED"; then
-   as_ac_Header=`$as_echo "ac_cv_header_$SV_MODULE_HEADER" | $as_tr_sh`
-ac_fn_cxx_check_header_mongrel "$LINENO" "$SV_MODULE_HEADER" "$as_ac_Header" "$ac_includes_default"
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
-  HAVES="$HAVES $SV_MODULE_HAVE";SV_MODULE_FAILED=""
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find header $SV_MODULE_HEADER for optional module $SV_MODULE_MODULE" >&5
-$as_echo "$as_me: Failed to find header $SV_MODULE_HEADER for optional module $SV_MODULE_MODULE" >&6;}
-fi
-
-
-   if test -z "$SV_MODULE_FAILED"; then
-      if test -n "$SV_MODULE_LIB"; then
-           as_ac_Lib=`$as_echo "ac_cv_lib_$SV_MODULE_LIB''_$SV_MODULE_FUNC" | $as_tr_sh`
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB" >&5
-$as_echo_n "checking for $SV_MODULE_FUNC in -l$SV_MODULE_LIB... " >&6; }
-if eval \${$as_ac_Lib+:} false; then :
-  $as_echo_n "(cached) " >&6
-else
-  ac_check_lib_save_LIBS=$LIBS
-LIBS="-l$SV_MODULE_LIB  $LIBS"
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-/* Override any GCC internal prototype to avoid an error.
-   Use char because int might match the return type of a GCC
-   builtin and then its argument prototype would still apply.  */
-#ifdef __cplusplus
-extern "C"
-#endif
-char $SV_MODULE_FUNC ();
-int
-main ()
-{
-return $SV_MODULE_FUNC ();
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_cxx_try_link "$LINENO"; then :
-  eval "$as_ac_Lib=yes"
-else
-  eval "$as_ac_Lib=no"
-fi
-rm -f core conftest.err conftest.$ac_objext \
-    conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
-fi
-eval ac_res=\$$as_ac_Lib
-	       { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-if eval test \"x\$"$as_ac_Lib"\" = x"yes"; then :
-  LIBS="$LIBS -l$SV_MODULE_LIB"
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find library $SV_MODULE_LIB for optional module $SV_MODULE_MODULE" >&5
-$as_echo "$as_me: Failed to find library $SV_MODULE_LIB for optional module $SV_MODULE_MODULE" >&6;}
-fi
-
-      fi
-   fi
-fi
-
-
-SV_MODULE_MODULE=portaudio_2_0
-SV_MODULE_VERSION_TEST="portaudio-2.0 >= 19"
-SV_MODULE_HEADER=portaudio.h
-SV_MODULE_LIB=portaudio
-SV_MODULE_FUNC=Pa_IsFormatSupported
-SV_MODULE_HAVE=HAVE_$(echo portaudio_2_0 | tr 'a-z' 'A-Z')
-SV_MODULE_FAILED=1
-if test -n "$portaudio_2_0_LIBS" ; then
-   { $as_echo "$as_me:${as_lineno-$LINENO}: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&5
-$as_echo "$as_me: User set ${SV_MODULE_MODULE}_LIBS explicitly, skipping test for $SV_MODULE_MODULE" >&6;}
-   CXXFLAGS="$CXXFLAGS $portaudio_2_0_CFLAGS"
-   LIBS="$LIBS $portaudio_2_0_LIBS"
-   SV_MODULE_FAILED=""
-fi
-if test -z "$SV_MODULE_VERSION_TEST" ; then
-   SV_MODULE_VERSION_TEST=$SV_MODULE_MODULE
-fi
-if test -n "$SV_MODULE_FAILED" && test -n "$PKG_CONFIG"; then
-
-pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for portaudio_2_0" >&5
-$as_echo_n "checking for portaudio_2_0... " >&6; }
-
-if test -n "$portaudio_2_0_CFLAGS"; then
-    pkg_cv_portaudio_2_0_CFLAGS="$portaudio_2_0_CFLAGS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_portaudio_2_0_CFLAGS=`$PKG_CONFIG --cflags "$SV_MODULE_VERSION_TEST" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
-else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
-fi
-if test -n "$portaudio_2_0_LIBS"; then
-    pkg_cv_portaudio_2_0_LIBS="$portaudio_2_0_LIBS"
- elif test -n "$PKG_CONFIG"; then
-    if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists --print-errors \"\$SV_MODULE_VERSION_TEST\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "$SV_MODULE_VERSION_TEST") 2>&5
-  ac_status=$?
-  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
-  test $ac_status = 0; }; then
-  pkg_cv_portaudio_2_0_LIBS=`$PKG_CONFIG --libs "$SV_MODULE_VERSION_TEST" 2>/dev/null`
-		      test "x$?" != "x0" && pkg_failed=yes
-else
-  pkg_failed=yes
-fi
- else
-    pkg_failed=untried
-fi
-
-
-
-if test $pkg_failed = yes; then
-   	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-
-if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
-        _pkg_short_errors_supported=yes
-else
-        _pkg_short_errors_supported=no
-fi
-        if test $_pkg_short_errors_supported = yes; then
-	        portaudio_2_0_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
-        else
-	        portaudio_2_0_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$SV_MODULE_VERSION_TEST" 2>&1`
-        fi
-	# Put the nasty error message in config.log where it belongs
-	echo "$portaudio_2_0_PKG_ERRORS" >&5
-
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
-$as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
-elif test $pkg_failed = untried; then
-     	{ $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-	{ $as_echo "$as_me:${as_lineno-$LINENO}: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&5
-$as_echo "$as_me: Failed to find optional module $SV_MODULE_MODULE using pkg-config, trying again by old-fashioned means" >&6;}
-else
-	portaudio_2_0_CFLAGS=$pkg_cv_portaudio_2_0_CFLAGS
-	portaudio_2_0_LIBS=$pkg_cv_portaudio_2_0_LIBS
-        { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-	HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $portaudio_2_0_CFLAGS";LIBS="$LIBS $portaudio_2_0_LIBS";SV_MODULE_FAILED=""
+	HAVES="$HAVES $SV_MODULE_HAVE";CXXFLAGS="$CXXFLAGS $portaudio_CFLAGS";LIBS="$LIBS $portaudio_LIBS";SV_MODULE_FAILED=""
 fi
 fi
 if test -n "$SV_MODULE_FAILED"; then
--- a/configure.ac	Tue Jul 14 15:04:45 2015 +0100
+++ b/configure.ac	Wed Apr 20 12:06:28 2016 +0100
@@ -25,6 +25,9 @@
 AC_PROG_INSTALL
 AC_PROG_MKDIR_P
 
+# We are daringly making use of C++11 now
+AX_CXX_COMPILE_STDCXX_11(noext)
+
 AC_HEADER_STDC
 
 # These are the flags Autoconf guesses for us; we use them later if
@@ -50,9 +53,10 @@
 CXXFLAGS_MINIMAL="$AUTOCONF_CXXFLAGS"
 
 if test "x$GCC" = "xyes"; then
-        CXXFLAGS_DEBUG="-Wall -Woverloaded-virtual -Wextra -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -g -pipe"
-   	CXXFLAGS_RELEASE="-g0 -O2 -Wall -pipe"
-   	CXXFLAGS_MINIMAL="-g0 -O0"
+   	CXXFLAGS_ANY="-Wall -Wextra -Woverloaded-virtual -Wformat-nonliteral -Wformat-security -Winit-self -Wswitch-enum -Wconversion -pipe"
+        CXXFLAGS_DEBUG="$CXXFLAGS_ANY -Werror -g"
+   	CXXFLAGS_RELEASE="$CXXFLAGS_ANY -g0 -O2"
+   	CXXFLAGS_MINIMAL="$CXXFLAGS_ANY -g0 -O0"
 fi
 
 CXXFLAGS_BUILD="$CXXFLAGS_RELEASE"
@@ -79,12 +83,10 @@
 SV_MODULE_REQUIRED([fftw3f],[fftw3f >= 3.0.0],[fftw3.h],[fftw3f],[fftwf_execute])
 SV_MODULE_REQUIRED([sndfile],[sndfile >= 1.0.16],[sndfile.h],[sndfile],[sf_open])
 SV_MODULE_REQUIRED([samplerate],[samplerate >= 0.1.2],[samplerate.h],[samplerate],[src_new])
-SV_MODULE_REQUIRED([vamp],[vamp >= 2.1],[vamp/vamp.h],[],[])
-SV_MODULE_REQUIRED([vamphostsdk],[vamp-hostsdk >= 2.5],[vamp-hostsdk/PluginLoader.h],[vamp-hostsdk],[libvamphostsdk_v_2_5_present])
 SV_MODULE_REQUIRED([rubberband],[rubberband],[rubberband/RubberBandStretcher.h],[rubberband],[rubberband_new])
 
 SV_MODULE_OPTIONAL([liblo],[],[lo/lo.h],[lo],[lo_address_new])
-SV_MODULE_OPTIONAL([portaudio_2_0],[portaudio-2.0 >= 19],[portaudio.h],[portaudio],[Pa_IsFormatSupported])
+SV_MODULE_OPTIONAL([portaudio],[portaudio-2.0 >= 19],[portaudio.h],[portaudio],[Pa_IsFormatSupported])
 SV_MODULE_OPTIONAL([JACK],[jack >= 0.100],[jack/jack.h],[jack],[jack_client_open])
 SV_MODULE_OPTIONAL([libpulse],[libpulse >= 0.9],[pulse/pulseaudio.h],[pulse],[pa_stream_new])
 SV_MODULE_OPTIONAL([lrdf],[lrdf >= 0.2],[lrdf.h],[lrdf],[lrdf_init])
--- a/framework/Document.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/Document.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -19,6 +19,8 @@
 #include "data/model/WritableWaveFileModel.h"
 #include "data/model/DenseThreeDimensionalModel.h"
 #include "data/model/DenseTimeValueModel.h"
+#include "data/model/FlexiNoteModel.h"
+
 #include "layer/Layer.h"
 #include "widgets/CommandHistory.h"
 #include "base/Command.h"
@@ -27,6 +29,7 @@
 #include "base/PlayParameters.h"
 #include "transform/TransformFactory.h"
 #include "transform/ModelTransformerFactory.h"
+#include "transform/FeatureExtractionModelTransformer.h"
 #include <QApplication>
 #include <QTextStream>
 #include <QSettings>
@@ -38,6 +41,8 @@
 #include "data/model/SparseTimeValueModel.h"
 #include "data/model/AlignmentModel.h"
 
+using std::vector;
+
 //#define DEBUG_DOCUMENT 1
 
 //!!! still need to handle command history, documentRestored/documentModified
@@ -46,9 +51,15 @@
     m_mainModel(0),
     m_autoAlignment(false)
 {
-    connect(this, SIGNAL(modelAboutToBeDeleted(Model *)),
+    connect(this,
+            SIGNAL(modelAboutToBeDeleted(Model *)),
             ModelTransformerFactory::getInstance(),
             SLOT(modelAboutToBeDeleted(Model *)));
+
+    connect(ModelTransformerFactory::getInstance(),
+            SIGNAL(transformFailed(QString, QString)),
+            this,
+            SIGNAL(modelGenerationFailed(QString, QString)));
 }
 
 Document::~Document()
@@ -100,7 +111,6 @@
 
     emit mainModelChanged(0);
     delete m_mainModel;
-
 }
 
 Layer *
@@ -207,56 +217,191 @@
 Document::createDerivedLayer(const Transform &transform,
                              const ModelTransformer::Input &input)
 {
+    Transforms transforms;
+    transforms.push_back(transform);
+    vector<Layer *> layers = createDerivedLayers(transforms, input);
+    if (layers.empty()) return 0;
+    else return layers[0];
+}
+
+vector<Layer *>
+Document::createDerivedLayers(const Transforms &transforms,
+                              const ModelTransformer::Input &input)
+{
     QString message;
-    Model *newModel = addDerivedModel(transform, input, message);
-    if (!newModel) {
-        emit modelGenerationFailed(transform.getIdentifier(), message);
-        return 0;
+    vector<Model *> newModels = addDerivedModels(transforms, input, message, 0);
+
+    if (newModels.empty()) {
+        //!!! This identifier may be wrong!
+        emit modelGenerationFailed(transforms[0].getIdentifier(), message);
+        return vector<Layer *>();
     } else if (message != "") {
-        emit modelGenerationWarning(transform.getIdentifier(), message);
+        //!!! This identifier may be wrong!
+        emit modelGenerationWarning(transforms[0].getIdentifier(), message);
     }
 
-    LayerFactory::LayerTypeSet types =
-	LayerFactory::getInstance()->getValidLayerTypes(newModel);
-
-    if (types.empty()) {
-	cerr << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << transform.getIdentifier() << endl;
-        newModel->aboutToDelete();
-        emit modelAboutToBeDeleted(newModel);
-        m_models.erase(newModel);
-	delete newModel;
-	return 0;
+    QStringList names;
+    for (int i = 0; i < (int)newModels.size(); ++i) {
+        names.push_back(getUniqueLayerName
+                        (TransformFactory::getInstance()->
+                         getTransformFriendlyName
+                         (transforms[i].getIdentifier())));
     }
 
-    //!!! for now, just use the first suitable layer type
+    vector<Layer *> layers = createLayersForDerivedModels(newModels, names);
+    return layers;
+}
 
-    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.getIdentifier())));
+class AdditionalModelConverter : 
+    public ModelTransformerFactory::AdditionalModelHandler
+{
+public:
+    AdditionalModelConverter(Document *doc, 
+                             Document::LayerCreationHandler *handler) :
+        m_doc(doc),
+        m_handler(handler) {
     }
 
-    emit layerAdded(newLayer);
-    return newLayer;
+    virtual ~AdditionalModelConverter() { }
+
+    void
+    setPrimaryLayers(vector<Layer *> layers) {
+        m_primary = layers;
+    }
+
+    void
+    moreModelsAvailable(vector<Model *> models) {
+        std::cerr << "AdditionalModelConverter::moreModelsAvailable: " << models.size() << " model(s)" << std::endl;
+        // We can't automatically regenerate the additional models on
+        // reload -- we should delete them instead
+        QStringList names;
+        foreach (Model *model, models) {
+            m_doc->addAdditionalModel(model);
+            names.push_back(QString());
+        }
+        vector<Layer *> layers = m_doc->createLayersForDerivedModels
+            (models, names);
+        m_handler->layersCreated(this, m_primary, layers);
+        delete this;
+    }
+
+    void
+    noMoreModelsAvailable() {
+        std::cerr << "AdditionalModelConverter::noMoreModelsAvailable" << std::endl;
+        m_handler->layersCreated(this, m_primary, vector<Layer *>());
+        delete this;
+    }
+
+    void cancel() {
+        foreach (Layer *layer, m_primary) {
+            Model *model = layer->getModel();
+            if (model) {
+                model->abandon();
+            }
+        }
+    }
+
+private:
+    Document *m_doc;
+    vector<Layer *> m_primary;
+    Document::LayerCreationHandler *m_handler; //!!! how to handle destruction of this?
+};
+
+Document::LayerCreationAsyncHandle
+Document::createDerivedLayersAsync(const Transforms &transforms,
+                                   const ModelTransformer::Input &input,
+                                   LayerCreationHandler *handler)
+{
+    QString message;
+
+    AdditionalModelConverter *amc = new AdditionalModelConverter(this, handler);
+    
+    vector<Model *> newModels = addDerivedModels
+        (transforms, input, message, amc);
+
+    QStringList names;
+    for (int i = 0; i < (int)newModels.size(); ++i) {
+        names.push_back(getUniqueLayerName
+                        (TransformFactory::getInstance()->
+                         getTransformFriendlyName
+                         (transforms[i].getIdentifier())));
+    }
+
+    vector<Layer *> layers = createLayersForDerivedModels(newModels, names);
+    amc->setPrimaryLayers(layers);
+
+    if (newModels.empty()) {
+        //!!! This identifier may be wrong!
+        emit modelGenerationFailed(transforms[0].getIdentifier(), message);
+        //!!! what to do with amc?
+    } else if (message != "") {
+        //!!! This identifier may be wrong!
+        emit modelGenerationWarning(transforms[0].getIdentifier(), message);
+        //!!! what to do with amc?
+    }
+
+    return amc;
+}
+
+void
+Document::cancelAsyncLayerCreation(Document::LayerCreationAsyncHandle h)
+{
+    AdditionalModelConverter *conv = static_cast<AdditionalModelConverter *>(h);
+    conv->cancel();
+}
+
+vector<Layer *>
+Document::createLayersForDerivedModels(vector<Model *> newModels, 
+                                       QStringList names)
+{
+    vector<Layer *> layers;
+    
+    for (int i = 0; i < (int)newModels.size(); ++i) {
+
+        Model *newModel = newModels[i];
+
+        LayerFactory::LayerTypeSet types =
+            LayerFactory::getInstance()->getValidLayerTypes(newModel);
+
+        if (types.empty()) {
+            cerr << "WARNING: Document::createLayerForTransformer: no valid display layer for output of transform " << names[i] << endl;
+            //!!! inadequate cleanup:
+            newModel->aboutToDelete();
+            emit modelAboutToBeDeleted(newModel);
+            m_models.erase(newModel);
+            delete newModel;
+            return vector<Layer *>();
+        }
+
+        //!!! 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(names[i]);
+        }
+
+        emit layerAdded(newLayer);
+        layers.push_back(newLayer);
+    }
+
+    return layers;
 }
 
 void
@@ -374,10 +519,9 @@
                           << typeid(*replacementModel).name() << ") in layer "
                           << layer << " (name " << layer->objectName() << ")"
                           << endl;
-#endif
+
                 RangeSummarisableTimeValueModel *rm =
                     dynamic_cast<RangeSummarisableTimeValueModel *>(replacementModel);
-#ifdef DEBUG_DOCUMENT
                 if (rm) {
                     cerr << "new model has " << rm->getChannelCount() << " channels " << endl;
                 } else {
@@ -394,6 +538,14 @@
     }
 
     for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
+        if (i->second.additional) {
+            Model *m = i->first;
+            emit modelAboutToBeDeleted(m);
+            delete m;
+        }
+    }
+
+    for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
 
         Model *m = i->first;
 
@@ -419,6 +571,7 @@
     }
 
     if (m_autoAlignment) {
+        SVDEBUG << "Document::setMainModel: auto-alignment is on, aligning model if possible" << endl;
         alignModel(m_mainModel);
     }
 
@@ -428,21 +581,21 @@
 }
 
 void
-Document::addDerivedModel(const Transform &transform,
-                          const ModelTransformer::Input &input,
-                          Model *outputModelToAdd)
+Document::addAlreadyDerivedModel(const Transform &transform,
+                                 const ModelTransformer::Input &input,
+                                 Model *outputModelToAdd)
 {
     if (m_models.find(outputModelToAdd) != m_models.end()) {
-	cerr << "WARNING: Document::addDerivedModel: Model already added"
+	cerr << "WARNING: Document::addAlreadyDerivedModel: Model already added"
 		  << endl;
 	return;
     }
 
 #ifdef DEBUG_DOCUMENT
     if (input.getModel()) {
-        cerr << "Document::addDerivedModel: source is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl;
+        cerr << "Document::addAlreadyDerivedModel: source is " << input.getModel() << " \"" << input.getModel()->objectName() << "\"" << endl;
     } else {
-        cerr << "Document::addDerivedModel: source is " << input.getModel() << endl;
+        cerr << "Document::addAlreadyDerivedModel: source is " << input.getModel() << endl;
     }
 #endif
 
@@ -450,6 +603,7 @@
     rec.source = input.getModel();
     rec.channel = input.getChannel();
     rec.transform = transform;
+    rec.additional = false;
     rec.refcount = 0;
 
     outputModelToAdd->setSourceModel(input.getModel());
@@ -457,7 +611,7 @@
     m_models[outputModelToAdd] = rec;
 
 #ifdef DEBUG_DOCUMENT
-    SVDEBUG << "Document::addDerivedModel: Added model " << outputModelToAdd << endl;
+    cerr << "Document::addAlreadyDerivedModel: Added model " << outputModelToAdd << endl;
     cerr << "Models now: ";
     for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) {
         cerr << i->first << " ";
@@ -480,7 +634,9 @@
 
     ModelRecord rec;
     rec.source = 0;
+    rec.channel = 0;
     rec.refcount = 0;
+    rec.additional = false;
 
     m_models[model] = rec;
 
@@ -493,7 +649,46 @@
     cerr << endl;
 #endif
 
-    if (m_autoAlignment) alignModel(model);
+    if (m_autoAlignment) {
+        SVDEBUG << "Document::addImportedModel: auto-alignment is on, aligning model if possible" << endl;
+        alignModel(model);
+    } else {
+        SVDEBUG << "Document(" << this << "): addImportedModel: auto-alignment is off" << endl;
+    }
+
+    emit modelAdded(model);
+}
+
+void
+Document::addAdditionalModel(Model *model)
+{
+    if (m_models.find(model) != m_models.end()) {
+	cerr << "WARNING: Document::addAdditionalModel: Model already added"
+		  << endl;
+	return;
+    }
+
+    ModelRecord rec;
+    rec.source = 0;
+    rec.channel = 0;
+    rec.refcount = 0;
+    rec.additional = true;
+
+    m_models[model] = rec;
+
+#ifdef DEBUG_DOCUMENT
+    SVDEBUG << "Document::addAdditionalModel: Added model " << model << endl;
+    cerr << "Models now: ";
+    for (ModelMap::const_iterator i = m_models.begin(); i != m_models.end(); ++i) {
+        cerr << i->first << " ";
+    } 
+    cerr << endl;
+#endif
+
+    if (m_autoAlignment) {
+        SVDEBUG << "Document::addAdditionalModel: auto-alignment is on, aligning model if possible" << endl;
+        alignModel(model);
+    }
 
     emit modelAdded(model);
 }
@@ -503,40 +698,59 @@
                           const ModelTransformer::Input &input,
                           QString &message)
 {
-    Model *model = 0;
-
     for (ModelMap::iterator i = m_models.begin(); i != m_models.end(); ++i) {
-	if (i->second.transform == transform &&
-	    i->second.source == input.getModel() && 
+        if (i->second.transform == transform &&
+            i->second.source == input.getModel() && 
             i->second.channel == input.getChannel()) {
-	    return i->first;
-	}
+            std::cerr << "derived model taken from map " << std::endl;
+            return i->first;
+        }
     }
 
-    model = ModelTransformerFactory::getInstance()->transform
-        (transform, input, message);
+    Transforms tt;
+    tt.push_back(transform);
+    vector<Model *> mm = addDerivedModels(tt, input, message, 0);
+    if (mm.empty()) return 0;
+    else return mm[0];
+}
 
-    // The transform we actually used was presumably identical to the
-    // one asked for, except that the version of the plugin may
-    // differ.  It's possible that the returned message contains a
-    // warning about this; that doesn't concern us here, but we do
-    // need to ensure that the transform we remember is correct for
-    // what was actually applied, with the current plugin version.
+vector<Model *>
+Document::addDerivedModels(const Transforms &transforms,
+                           const ModelTransformer::Input &input,
+                           QString &message,
+                           AdditionalModelConverter *amc)
+{
+    vector<Model *> mm = 
+        ModelTransformerFactory::getInstance()->transformMultiple
+        (transforms, input, message, amc);
 
-    Transform applied = transform;
-    applied.setPluginVersion
-        (TransformFactory::getInstance()->
-         getDefaultTransformFor(transform.getIdentifier(),
-                                lrintf(transform.getSampleRate()))
-         .getPluginVersion());
+    for (int j = 0; j < (int)mm.size(); ++j) {
 
-    if (!model) {
-	cerr << "WARNING: Document::addDerivedModel: no output model for transform " << transform.getIdentifier() << endl;
-    } else {
-	addDerivedModel(applied, input, model);
+        Model *model = mm[j];
+
+        // The transform we actually used was presumably identical to
+        // the one asked for, except that the version of the plugin
+        // may differ.  It's possible that the returned message
+        // contains a warning about this; that doesn't concern us
+        // here, but we do need to ensure that the transform we
+        // remember is correct for what was actually applied, with the
+        // current plugin version.
+
+        Transform applied = transforms[j];
+        applied.setPluginVersion
+            (TransformFactory::getInstance()->
+             getDefaultTransformFor(applied.getIdentifier(),
+                                    applied.getSampleRate())
+             .getPluginVersion());
+
+        if (!model) {
+            cerr << "WARNING: Document::addDerivedModel: no output model for transform " << applied.getIdentifier() << endl;
+        } else {
+            addAlreadyDerivedModel(applied, input, model);
+        }
     }
-
-    return model;
+	
+    return mm;
 }
 
 void
@@ -690,6 +904,7 @@
     }
 
     LayerFactory::getInstance()->setModel(layer, model);
+	// std::cerr << "layer type: " << LayerFactory::getInstance()->getLayerTypeName(LayerFactory::getInstance()->getLayerType(layer)) << std::endl;
 
     if (previousModel) {
         releaseModel(previousModel);
@@ -846,14 +1061,22 @@
 void
 Document::alignModel(Model *model)
 {
-    if (!m_mainModel) return;
+    SVDEBUG << "Document::alignModel(" << model << ")" << endl;
+
+    if (!m_mainModel) {
+        SVDEBUG << "(no main model to align to)" << endl;
+        return;
+    }
 
     RangeSummarisableTimeValueModel *rm = 
         dynamic_cast<RangeSummarisableTimeValueModel *>(model);
-    if (!rm) return;
+    if (!rm) {
+        SVDEBUG << "(main model is not alignable-to)" << endl;
+        return;
+    }
 
     if (rm->getAlignmentReference() == m_mainModel) {
-        SVDEBUG << "Document::alignModel: model " << rm << " is already aligned to main model " << m_mainModel << endl;
+        SVDEBUG << "(model " << rm << " is already aligned to main model " << m_mainModel << ")" << endl;
         return;
     }
     
@@ -1013,6 +1236,7 @@
     m_d(d),
     m_view(view),
     m_layer(layer),
+    m_wasDormant(layer->isLayerDormant(view)),
     m_name(qApp->translate("RemoveLayerCommand", "Delete %1 Layer").arg(layer->objectName())),
     m_added(true)
 {
@@ -1066,7 +1290,7 @@
 Document::RemoveLayerCommand::unexecute()
 {
     m_view->addLayer(m_layer);
-    m_layer->setLayerDormant(m_view, false);
+    m_layer->setLayerDormant(m_view, m_wasDormant);
 
     m_d->addToLayerViewMap(m_layer, m_view);
     m_added = true;
--- a/framework/Document.h	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/Document.h	Wed Apr 20 12:06:28 2016 +0100
@@ -19,6 +19,7 @@
 #include "layer/LayerFactory.h"
 #include "transform/Transform.h"
 #include "transform/ModelTransformer.h"
+#include "transform/FeatureExtractionModelTransformer.h"
 #include "base/Command.h"
 
 #include <map>
@@ -29,6 +30,8 @@
 class View;
 class WaveFileModel;
 
+class AdditionalModelConverter;
+
 /**
  * A Sonic Visualiser document consists of a set of data models, and
  * also the visualisation layers used to display them.  Changes to the
@@ -117,6 +120,56 @@
                               const ModelTransformer::Input &);
 
     /**
+     * Create and return suitable layers for the given transforms,
+     * which must be identical apart from the output (i.e. must use
+     * the same plugin and configuration). The layers are returned in
+     * the same order as the transforms are supplied.
+     */
+    std::vector<Layer *> createDerivedLayers(const Transforms &,
+                                             const ModelTransformer::Input &);
+
+    typedef void *LayerCreationAsyncHandle;
+
+    class LayerCreationHandler {
+    public:
+        virtual ~LayerCreationHandler() { }
+
+        /**
+         * The primary layers are those corresponding 1-1 to the input
+         * models, listed in the same order as the input models. The
+         * additional layers vector contains any layers (from all
+         * models) that were returned separately at the end of
+         * processing. The handle indicates which async process this
+         * callback was initiated by. It must not be used again after
+         * this function returns.
+         */
+        virtual void layersCreated(LayerCreationAsyncHandle handle,
+                                   std::vector<Layer *> primary,
+                                   std::vector<Layer *> additional) = 0;
+    };
+
+    /**
+     * Create suitable layers for the given transforms, which must be
+     * identical apart from the output (i.e. must use the same plugin
+     * and configuration). This method returns after initialising the
+     * transformer process, and the layers are returned through a
+     * subsequent call to the provided handler (which must be
+     * non-null). The handle returned will be passed through to the
+     * handler callback, and may be also used for cancelling the task.
+     */
+    LayerCreationAsyncHandle createDerivedLayersAsync(const Transforms &,
+                                                      const ModelTransformer::Input &,
+                                                      LayerCreationHandler *handler);
+
+    /**
+     * Indicate that the async layer creation task associated with the
+     * given handle should be cancelled. There is no guarantee about
+     * what this will mean, and the handler callback may still be
+     * called.
+     */
+    void cancelAsyncLayerCreation(LayerCreationAsyncHandle handle);
+
+    /**
      * 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
@@ -154,13 +207,24 @@
                            QString &returnedMessage);
 
     /**
+     * Add derived models associated with the given set of related
+     * transforms, running the transforms and returning the resulting
+     * models.
+     */
+    friend class AdditionalModelConverter;
+    std::vector<Model *> addDerivedModels(const Transforms &transforms,
+                                          const ModelTransformer::Input &input,
+                                          QString &returnedMessage,
+                                          AdditionalModelConverter *);
+
+    /**
      * 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(const Transform &transform,
-                         const ModelTransformer::Input &input,
-                         Model *outputModelToAdd);
+    void addAlreadyDerivedModel(const Transform &transform,
+                                const ModelTransformer::Input &input,
+                                Model *outputModelToAdd);
 
     /**
      * Add an imported (non-derived, non-main) model.  This is
@@ -190,7 +254,9 @@
     void addLayerToView(View *, Layer *);
 
     /**
-     * Remove the given layer from the given view.
+     * Remove the given layer from the given view. Ownership of the
+     * layer is transferred to a command object on the undo stack, and
+     * the layer will be deleted when the undo stack is pruned.
      */
     void removeLayerFromView(View *, Layer *);
 
@@ -279,6 +345,7 @@
 	const Model *source;
         int channel;
         Transform transform;
+        bool additional;
 
 	// Count of the number of layers using this model.
 	int refcount;
@@ -287,6 +354,12 @@
     typedef std::map<Model *, ModelRecord> ModelMap;
     ModelMap m_models;
 
+    /**
+     * Add an extra derived model (returned at the end of processing a
+     * transform).
+     */
+    void addAdditionalModel(Model *);
+
     class AddLayerCommand : public Command
     {
     public:
@@ -319,6 +392,7 @@
 	Document *m_d;
 	View *m_view; // I don't own this
 	Layer *m_layer; // Document owns this, but I determine its lifespan
+        bool m_wasDormant;
 	QString m_name;
 	bool m_added;
     };
@@ -338,6 +412,9 @@
     void toXml(QTextStream &, QString, QString, bool asTemplate) const;
     void writePlaceholderMainModel(QTextStream &, QString) const;
 
+    std::vector<Layer *> createLayersForDerivedModels(std::vector<Model *>,
+                                                      QStringList names);
+
     /**
      * And these are the layers.  We also control the lifespans of
      * these (usually through the commands used to add and remove them).
--- a/framework/MainWindowBase.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/MainWindowBase.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -16,12 +16,13 @@
 #include "MainWindowBase.h"
 #include "Document.h"
 
-
 #include "view/Pane.h"
 #include "view/PaneStack.h"
-#include "data/model/WaveFileModel.h"
+#include "data/model/ReadOnlyWaveFileModel.h"
+#include "data/model/WritableWaveFileModel.h"
 #include "data/model/SparseOneDimensionalModel.h"
 #include "data/model/NoteModel.h"
+#include "data/model/FlexiNoteModel.h"
 #include "data/model/Labeller.h"
 #include "data/model/TabularModel.h"
 #include "view/ViewManager.h"
@@ -35,6 +36,7 @@
 #include "layer/SliceableLayer.h"
 #include "layer/ImageLayer.h"
 #include "layer/NoteLayer.h"
+#include "layer/FlexiNoteLayer.h"
 #include "layer/RegionLayer.h"
 
 #include "widgets/ListInputDialog.h"
@@ -43,23 +45,21 @@
 #include "widgets/MIDIFileImportDialog.h"
 #include "widgets/CSVFormatDialog.h"
 #include "widgets/ModelDataTableDialog.h"
-
-#include "audioio/AudioCallbackPlaySource.h"
-#include "audioio/AudioCallbackPlayTarget.h"
-#include "audioio/AudioTargetFactory.h"
-#include "audioio/PlaySpeedRangeMapper.h"
+#include "widgets/InteractiveFileFinder.h"
+
+#include "audio/AudioCallbackPlaySource.h"
+#include "audio/AudioRecordTarget.h"
+#include "audio/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/fileio/AudioFileReaderFactory.h"
 #include "rdf/RDFImporter.h"
 
-#include "data/fft/FFTDataServer.h"
-
 #include "base/RecentFiles.h"
 
 #include "base/PlayParameterRepository.h"
@@ -73,6 +73,10 @@
 #include "data/osc/OSCQueue.h"
 #include "data/midi/MIDIInput.h"
 
+#include <bqaudioio/SystemPlaybackTarget.h>
+#include <bqaudioio/SystemAudioIO.h>
+#include <bqaudioio/AudioFactory.h>
+
 #include <QApplication>
 #include <QMessageBox>
 #include <QGridLayout>
@@ -87,6 +91,7 @@
 #include <QFileInfo>
 #include <QDir>
 #include <QTextStream>
+#include <QTextCodec>
 #include <QProcess>
 #include <QShortcut>
 #include <QSettings>
@@ -96,14 +101,12 @@
 #include <QRegExp>
 #include <QScrollArea>
 #include <QDesktopWidget>
+#include <QSignalMapper>
 
 #include <iostream>
 #include <cstdio>
 #include <errno.h>
 
-
-
-
 using std::vector;
 using std::map;
 using std::set;
@@ -130,16 +133,16 @@
 #undef Window
 #endif
 
-MainWindowBase::MainWindowBase(bool withAudioOutput,
-                               bool withOSCSupport,
-                               bool withMIDIInput) :
+MainWindowBase::MainWindowBase(SoundOptions options) :
     m_document(0),
     m_paneStack(0),
     m_viewManager(0),
     m_timeRulerLayer(0),
-    m_audioOutput(withAudioOutput),
+    m_soundOptions(options),
     m_playSource(0),
+    m_recordTarget(0),
     m_playTarget(0),
+    m_audioIO(0),
     m_oscQueue(0),
     m_oscQueueStarter(0),
     m_midiInput(0),
@@ -149,14 +152,30 @@
     m_openingAudioFile(false),
     m_abandoning(false),
     m_labeller(0),
-    m_lastPlayStatusSec(0)
+    m_lastPlayStatusSec(0),
+    m_initialDarkBackground(false),
+    m_defaultFfwdRwdStep(2, 0),
+    m_audioRecordMode(RecordCreateAdditionalModel),
+    m_statusLabel(0),
+    m_menuShortcutMapper(0)
 {
     Profiler profiler("MainWindowBase::MainWindowBase");
 
+    if (options & WithAudioInput) {
+        if (!(options & WithAudioOutput)) {
+            cerr << "WARNING: MainWindowBase: WithAudioInput requires WithAudioOutput -- recording will not work" << endl;
+        }
+    }
+    
+    qRegisterMetaType<sv_frame_t>("sv_frame_t");
+    qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t");
+
 #ifdef Q_WS_X11
     XSetErrorHandler(handle_x11_error);
 #endif
 
+    connect(this, SIGNAL(hideSplash()), this, SLOT(emitHideSplash()));
+    
     connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
 	    this, SLOT(documentModified()));
     connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
@@ -170,13 +189,14 @@
 
     // set a sensible default font size for views -- cannot do this
     // in Preferences, which is in base and not supposed to use QtGui
-    int viewFontSize = QApplication::font().pointSize() * 0.9;
+    int viewFontSize = int(QApplication::font().pointSize() * 0.9);
     QSettings settings;
     settings.beginGroup("Preferences");
     viewFontSize = settings.value("view-font-size", viewFontSize).toInt();
     settings.setValue("view-font-size", viewFontSize);
     settings.endGroup();
 
+#ifdef NOT_DEFINED // This no longer works correctly on any platform AFAICS
     Preferences::BackgroundMode mode =
         Preferences::getInstance()->getBackgroundMode();
     m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
@@ -184,6 +204,7 @@
         m_viewManager->setGlobalDarkBackground
             (mode == Preferences::DarkBackground);
     }
+#endif
 
     m_paneStack = new PaneStack(0, m_viewManager);
     connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
@@ -209,9 +230,15 @@
 
     m_playSource = new AudioCallbackPlaySource(m_viewManager,
                                                QApplication::applicationName());
-
-    connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)),
-	    this,           SLOT(sampleRateMismatch(size_t, size_t, bool)));
+    if (m_soundOptions & WithAudioInput) {
+        m_recordTarget = new AudioRecordTarget(m_viewManager,
+                                               QApplication::applicationName());
+        connect(m_recordTarget, SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)),
+                this, SLOT(recordDurationChanged(sv_frame_t, sv_samplerate_t)));
+    }
+
+    connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)),
+	    this,           SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)));
     connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
             this,           SLOT(audioOverloadPluginDisabled()));
     connect(m_playSource, SIGNAL(audioTimeStretchMultiChannelDisabled()),
@@ -220,17 +247,17 @@
     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(m_viewManager, SIGNAL(playbackFrameChanged(sv_frame_t)),
+            this, SLOT(playbackFrameChanged(sv_frame_t)));
+
+    connect(m_viewManager, SIGNAL(globalCentreFrameChanged(sv_frame_t)),
+            this, SLOT(globalCentreFrameChanged(sv_frame_t)));
+
+    connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, sv_frame_t)),
+            this, SLOT(viewCentreFrameChanged(View *, sv_frame_t)));
+
+    connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, int, bool)),
+            this, SLOT(viewZoomLevelChanged(View *, int, bool)));
 
     connect(Preferences::getInstance(),
             SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
@@ -249,30 +276,171 @@
     m_labeller = new Labeller(labellerType);
     m_labeller->setCounterCycleSize(cycle);
 
-    if (withMIDIInput) {
+    if (m_soundOptions & WithMIDIInput) {
         m_midiInput = new MIDIInput(QApplication::applicationName(), this);
     }
 
-    if (withOSCSupport) {
-        m_oscQueueStarter = new OSCQueueStarter(this);
-        connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady()));
-        m_oscQueueStarter->start();
-    }
+    QTimer::singleShot(1500, this, SIGNAL(hideSplash()));
 }
 
 MainWindowBase::~MainWindowBase()
 {
     SVDEBUG << "MainWindowBase::~MainWindowBase" << endl;
-    if (m_playTarget) m_playTarget->shutdown();
-//    delete m_playTarget;
+    delete m_playTarget;
     delete m_playSource;
+    delete m_audioIO;
+    delete m_recordTarget;
     delete m_viewManager;
     delete m_oscQueue;
+    delete m_oscQueueStarter;
     delete m_midiInput;
     Profiles::getInstance()->dump();
 }
 
 void
+MainWindowBase::emitHideSplash()
+{
+    emit hideSplash(this);
+}
+
+void
+MainWindowBase::finaliseMenus()
+{
+    delete m_menuShortcutMapper;
+    m_menuShortcutMapper = 0;
+
+    foreach (QShortcut *sc, m_appShortcuts) {
+        delete sc;
+    }
+    m_appShortcuts.clear();
+
+    QMenuBar *mb = menuBar();
+
+    // This used to find all children of QMenu type, and call
+    // finaliseMenu on those. But it seems we are getting hold of some
+    // menus that way that are not actually active in the menu bar and
+    // are not returned in their parent menu's actions() list, and if
+    // we finalise those, we end up with duplicate shortcuts in the
+    // app shortcut mapper. So we should do this by descending the
+    // menu tree through only those menus accessible via actions()
+    // from their parents instead.
+
+    QList<QMenu *> menus = mb->findChildren<QMenu *>
+        (QString(), Qt::FindDirectChildrenOnly);
+
+    foreach (QMenu *menu, menus) {
+        if (menu) finaliseMenu(menu);
+    }
+}
+
+void
+MainWindowBase::finaliseMenu(QMenu *
+#ifdef Q_OS_MAC
+                             menu
+#endif
+    )
+{
+#ifdef Q_OS_MAC
+    // See https://bugreports.qt-project.org/browse/QTBUG-38256 and
+    // our issue #890 http://code.soundsoftware.ac.uk/issues/890 --
+    // single-key shortcuts that are associated only with a menu
+    // action (and not with a toolbar button) do not work with Qt 5.x
+    // under OS/X.
+    // 
+    // Apparently Cocoa never handled them as a matter of course, but
+    // earlier versions of Qt picked them up as widget shortcuts and
+    // handled them anyway. That behaviour was removed to fix a crash
+    // when invoking a menu while its window was overridden by a modal
+    // dialog (https://bugreports.qt-project.org/browse/QTBUG-30657).
+    //
+    // This workaround restores the single-key shortcut behaviour by
+    // searching in menus for single-key shortcuts that are associated
+    // only with the menu and not with a toolbar button, and
+    // augmenting them with global application shortcuts that invoke
+    // the relevant actions, testing whether the actions are enabled
+    // on invocation.
+    //
+    // (Previously this acted on all single-key shortcuts in menus,
+    // and it removed the shortcut from the action when it created
+    // each new global one, in order to avoid an "ambiguous shortcut"
+    // error in the case where the action was also associated with a
+    // toolbar button. But that has the unwelcome side-effect of
+    // removing the shortcut hint from the menu entry. So now we leave
+    // the shortcut in the menu action as well as creating a global
+    // one, and we only act on shortcuts that have no toolbar button,
+    // i.e. that will not otherwise work. The downside is that if this
+    // bug is fixed in a future Qt release, we will start getting
+    // "ambiguous shortcut" errors from the menu entry actions and
+    // will need to update the code.)
+
+    // Update: The bug was fixed in Qt 5.4 for shortcuts with no
+    // modifier, and I believe it is fixed in Qt 5.5 for shortcuts
+    // with Shift modifiers. The below reflects that
+
+#if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
+
+    if (!m_menuShortcutMapper) {
+        m_menuShortcutMapper = new QSignalMapper(this);
+        connect(m_menuShortcutMapper, SIGNAL(mapped(QObject *)),
+                this, SLOT(menuActionMapperInvoked(QObject *)));
+    }
+
+    foreach (QAction *a, menu->actions()) {
+
+        if (a->isSeparator()) {
+            continue;
+        } else if (a->menu()) {
+            finaliseMenu(a->menu());
+        } else {
+
+            QWidgetList ww = a->associatedWidgets();
+            bool hasButton = false;
+            foreach (QWidget *w, ww) {
+                if (qobject_cast<QAbstractButton *>(w)) {
+                    hasButton = true;
+                    break;
+                }
+            }
+            if (hasButton) continue;
+            QKeySequence sc = a->shortcut();
+
+            // Note that the set of "single-key shortcuts" that aren't
+            // working and that we need to handle here includes those
+            // with the Shift modifier mask as well as those with no
+            // modifier at all
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
+            // Nothing needed
+            if (false) {
+#elif (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
+            if (sc.count() == 1 &&
+                (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier) {
+#else
+            if (sc.count() == 1 &&
+                ((sc[0] & Qt::KeyboardModifierMask) == Qt::NoModifier ||
+                 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier)) {
+#endif
+                QShortcut *newSc = new QShortcut(sc, a->parentWidget());
+                QObject::connect(newSc, SIGNAL(activated()),
+                                 m_menuShortcutMapper, SLOT(map()));
+                m_menuShortcutMapper->setMapping(newSc, a);
+                m_appShortcuts.push_back(newSc);
+            }
+        }
+    }
+#endif
+#endif
+}
+
+void
+MainWindowBase::menuActionMapperInvoked(QObject *o)
+{
+    QAction *a = qobject_cast<QAction *>(o);
+    if (a && a->isEnabled()) {
+        a->trigger();
+    }
+}
+
+void
 MainWindowBase::resizeConstrained(QSize size)
 {
     QDesktopWidget *desktop = QApplication::desktop();
@@ -283,6 +451,14 @@
 }
 
 void
+MainWindowBase::startOSCQueue()
+{
+    m_oscQueueStarter = new OSCQueueStarter(this);
+    connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady()));
+    m_oscQueueStarter->start();
+}
+
+void
 MainWindowBase::oscReady()
 {
     if (m_oscQueue && m_oscQueue->isOK()) {
@@ -298,24 +474,8 @@
 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::CSVFile:
-        return ff->getOpenFileName(type, m_sessionFile);
-    case FileFinder::IMAFile:
-        return ff->getOpenFileName(type, m_audioFile);
-    case FileFinder::AnyFile:
+
+    if (type == FileFinder::AnyFile) {
         if (getMainModel() != 0 &&
             m_paneStack != 0 &&
             m_paneStack->getCurrentPane() != 0) { // can import a layer
@@ -324,33 +484,29 @@
             return ff->getOpenFileName(FileFinder::SessionOrAudioFile,
                                        m_sessionFile);
         }
+    }        
+
+    QString lastPath = m_sessionFile;
+
+    if (type == FileFinder::AudioFile ||
+        type == FileFinder::IMAFile) {
+        lastPath = m_audioFile;
     }
-    return "";
+
+    return ff->getOpenFileName(type, lastPath);
 }
 
 QString
 MainWindowBase::getSaveFileName(FileFinder::FileType type)
 {
+    QString lastPath = m_sessionFile;
+
+    if (type == FileFinder::AudioFile) {
+        lastPath = m_audioFile;
+    }
+
     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::CSVFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    case FileFinder::AnyFile:
-        return ff->getSaveFileName(type, m_sessionFile);
-    }
-    return "";
+    return ff->getSaveFileName(type, lastPath);
 }
 
 void
@@ -398,14 +554,13 @@
                 break;
             }
         }
-        if (currentLayer) {
-            for (int i = 0; i < currentPane->getLayerCount(); ++i) {
-                if (currentPane->getLayer(i) == currentLayer) {
-                    if (i > 0) havePrevLayer = true;
-                    if (i < currentPane->getLayerCount()-1) haveNextLayer = true;
-                    break;
-                }
-            }
+        // the prev/next layer commands actually include the pane
+        // itself as one of the selectables -- so we always have a
+        // prev and next layer, as long as we have a pane with at
+        // least one layer in it
+        if (currentPane->getLayerCount() > 0) {
+            havePrevLayer = true;
+            haveNextLayer = true;
         }
     }        
 
@@ -417,7 +572,7 @@
     bool haveMainModel =
 	(getMainModel() != 0);
     bool havePlayTarget =
-	(m_playTarget != 0);
+	(m_playTarget != 0 || m_audioIO != 0);
     bool haveSelection = 
 	(m_viewManager &&
 	 !m_viewManager->getSelections().empty());
@@ -430,6 +585,7 @@
     bool haveCurrentDurationLayer = 
 	(haveCurrentLayer &&
 	 (dynamic_cast<NoteLayer *>(currentLayer) ||
+	  dynamic_cast<FlexiNoteLayer *>(currentLayer) ||
           dynamic_cast<RegionLayer *>(currentLayer)));
     bool haveCurrentColour3DPlot =
         (haveCurrentLayer &&
@@ -461,8 +617,9 @@
     emit canMeasureLayer(haveCurrentLayer);
     emit canSelect(haveMainModel && haveCurrentPane);
     emit canPlay(haveMainModel && havePlayTarget);
-    emit canFfwd(true);
-    emit canRewind(true);
+    emit canRecord(m_recordTarget != 0);
+    emit canFfwd(haveMainModel);
+    emit canRewind(haveMainModel);
     emit canPaste(haveClipboardContents);
     emit canInsertInstant(haveCurrentPane);
     emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
@@ -472,6 +629,7 @@
     emit canClearSelection(haveSelection);
     emit canEditSelection(haveSelection && haveCurrentEditableLayer);
     emit canSave(m_sessionFile != "" && m_documentModified);
+    emit canSaveAs(haveMainModel);
     emit canSelectPreviousPane(havePrevPane);
     emit canSelectNextPane(haveNextPane);
     emit canSelectPreviousLayer(havePrevLayer);
@@ -578,9 +736,9 @@
     // frame before switching to whichever one we decide we want to
     // switch to, regardless of our efforts.
 
-    int frame = m_playSource->getCurrentBufferedFrame();
-
-//    cerr << "currentPaneChanged: current frame (in ref model) = " << frame << endl;
+    sv_frame_t frame = m_playSource->getCurrentBufferedFrame();
+
+    cerr << "currentPaneChanged: current frame (in ref model) = " << frame << endl;
 
     View::ModelSet soloModels = p->getModels();
     
@@ -667,7 +825,7 @@
     Pane *currentPane = m_paneStack->getCurrentPane();
     if (!currentPane) return;
 
-    size_t startFrame, endFrame;
+    sv_frame_t startFrame, endFrame;
 
     if (currentPane->getStartFrame() < 0) startFrame = 0;
     else startFrame = currentPane->getStartFrame();
@@ -738,24 +896,24 @@
 void
 MainWindowBase::pasteAtPlaybackPosition()
 {
-    unsigned long pos = getFrame();
+    sv_frame_t pos = getFrame();
     Clipboard &clipboard = m_viewManager->getClipboard();
     if (!clipboard.empty()) {
-        long firstEventFrame = clipboard.getPoints()[0].getFrame();
-        long offset = 0;
+        sv_frame_t firstEventFrame = clipboard.getPoints()[0].getFrame();
+        sv_frame_t offset = 0;
         if (firstEventFrame < 0) {
-            offset = (long)pos - firstEventFrame;
+            offset = pos - firstEventFrame;
         } else if (firstEventFrame < pos) {
-            offset = pos - (unsigned long)firstEventFrame;
+            offset = pos - firstEventFrame;
         } else {
-            offset = -((unsigned long)firstEventFrame - pos);
+            offset = -(firstEventFrame - pos);
         }
         pasteRelative(offset);
     }
 }
 
 void
-MainWindowBase::pasteRelative(int offset)
+MainWindowBase::pasteRelative(sv_frame_t offset)
 {
     Pane *currentPane = m_paneStack->getCurrentPane();
     if (!currentPane) return;
@@ -801,19 +959,21 @@
         
         Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer();
 
-        if (m_viewManager && 
-            (m_viewManager->getToolMode() == ViewManager::MeasureMode)) {
-
-            layer->deleteCurrentMeasureRect();
-
-        } else {
-
-            MultiSelection::SelectionList selections =
-                m_viewManager->getSelections();
+        if (m_viewManager) {
+
+            if (m_viewManager->getToolMode() == ViewManager::MeasureMode) {
+
+                layer->deleteCurrentMeasureRect();
             
-            for (MultiSelection::SelectionList::iterator i = selections.begin();
-                 i != selections.end(); ++i) {
-                layer->deleteSelection(*i);
+            } else {
+
+                MultiSelection::SelectionList selections =
+                    m_viewManager->getSelections();
+            
+                for (MultiSelection::SelectionList::iterator i = selections.begin();
+                     i != selections.end(); ++i) {
+                    layer->deleteSelection(*i);
+                }
             }
 	}
     }
@@ -821,7 +981,7 @@
 
 // FrameTimer method
 
-unsigned long
+sv_frame_t
 MainWindowBase::getFrame() const
 {
     if (m_playSource && m_playSource->isPlaying()) {
@@ -843,8 +1003,8 @@
     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();
+        sv_frame_t start = i->getStartFrame();
+        sv_frame_t end = i->getEndFrame();
         if (start != end) {
             insertInstantAt(start);
             insertInstantAt(end);
@@ -853,7 +1013,7 @@
 }
 
 void
-MainWindowBase::insertInstantAt(size_t frame)
+MainWindowBase::insertInstantAt(sv_frame_t frame)
 {
     Pane *pane = m_paneStack->getCurrentPane();
     if (!pane) {
@@ -898,29 +1058,29 @@
             SparseOneDimensionalModel::EditCommand *command =
                 new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point"));
 
-            if (m_labeller->requiresPrevPoint()) {
-
-                SparseOneDimensionalModel::PointList prevPoints =
-                    sodm->getPreviousPoints(frame);
-
-                if (!prevPoints.empty()) {
-                    prevPoint = *prevPoints.begin();
-                    havePrevPoint = true;
+            if (m_labeller) {
+
+                if (m_labeller->requiresPrevPoint()) {
+
+                    SparseOneDimensionalModel::PointList prevPoints =
+                        sodm->getPreviousPoints(frame);
+
+                    if (!prevPoints.empty()) {
+                        prevPoint = *prevPoints.begin();
+                        havePrevPoint = true;
+                    }
                 }
-            }
-
-            if (m_labeller) {
 
                 m_labeller->setSampleRate(sodm->getSampleRate());
 
-                if (m_labeller->actingOnPrevPoint()) {
+                if (m_labeller->actingOnPrevPoint() && havePrevPoint) {
                     command->deletePoint(prevPoint);
                 }
 
                 m_labeller->label<SparseOneDimensionalModel::Point>
                     (point, havePrevPoint ? &prevPoint : 0);
 
-                if (m_labeller->actingOnPrevPoint()) {
+                if (m_labeller->actingOnPrevPoint() && havePrevPoint) {
                     command->addPoint(prevPoint);
                 }
             }
@@ -945,8 +1105,8 @@
     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();
+        sv_frame_t start = i->getStartFrame();
+        sv_frame_t end = i->getEndFrame();
         if (start < end) {
             insertItemAt(start, end - start);
         }
@@ -954,7 +1114,7 @@
 }
 
 void
-MainWindowBase::insertItemAt(size_t frame, size_t duration)
+MainWindowBase::insertItemAt(sv_frame_t frame, sv_frame_t duration)
 {
     Pane *pane = m_paneStack->getCurrentPane();
     if (!pane) {
@@ -963,10 +1123,10 @@
 
     // ugh!
 
-    size_t alignedStart = pane->alignFromReference(frame);
-    size_t alignedEnd = pane->alignFromReference(frame + duration);
+    sv_frame_t alignedStart = pane->alignFromReference(frame);
+    sv_frame_t alignedEnd = pane->alignFromReference(frame + duration);
     if (alignedStart >= alignedEnd) return;
-    size_t alignedDuration = alignedEnd - alignedStart;
+    sv_frame_t alignedDuration = alignedEnd - alignedStart;
 
     Command *c = 0;
 
@@ -1000,7 +1160,7 @@
     NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel());
     if (nm) {
         NoteModel::Point point(alignedStart,
-                               rm->getValueMinimum(),
+                               nm->getValueMinimum(),
                                alignedDuration,
                                1.f,
                                "");
@@ -1015,6 +1175,25 @@
         CommandHistory::getInstance()->addCommand(c, false);
         return;
     }
+
+    FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(layer->getModel());
+    if (fnm) {
+        FlexiNoteModel::Point point(alignedStart,
+                                    fnm->getValueMinimum(),
+                                    alignedDuration,
+                                    1.f,
+                                    "");
+        FlexiNoteModel::EditCommand *command =
+            new FlexiNoteModel::EditCommand(fnm, tr("Add Point"));
+        command->addPoint(point);
+        command->setName(name);
+        c = command->finish();
+    }
+    
+    if (c) {
+        CommandHistory::getInstance()->addCommand(c, false);
+        return;
+    }
 }
 
 void
@@ -1044,7 +1223,7 @@
 }
 
 MainWindowBase::FileOpenStatus
-MainWindowBase::open(QString fileOrUrl, AudioFileOpenMode mode)
+MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
 {
     ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this);
     connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
@@ -1075,7 +1254,6 @@
         RDFImporter::RDFDocumentType rdfType = 
             RDFImporter::identifyDocumentType
             (QUrl::fromLocalFile(source.getLocalFilename()).toString());
-//        cerr << "RDF type: " << (int)rdfType << endl;
         if (rdfType == RDFImporter::AudioRefAndAnnotations ||
             rdfType == RDFImporter::AudioRef) {
             rdfSession = true;
@@ -1128,31 +1306,44 @@
 MainWindowBase::openAudio(FileSource source, AudioFileOpenMode mode,
                           QString templateName)
 {
-//    SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ")" << endl;
+    SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
 
     if (templateName == "") {
         templateName = getDefaultSessionTemplate();
     }
 
-    cerr << "template is: \"" << templateName << "\"" << endl;
-
-    if (!source.isAvailable()) return FileOpenFailed;
+//    cerr << "template is: \"" << templateName << "\"" << endl;
+
+    if (!source.isAvailable()) {
+        if (source.wasCancelled()) {
+            return FileOpenCancelled;
+        } else {
+            return FileOpenFailed;
+        }
+    }
+
     source.waitForData();
 
     m_openingAudioFile = true;
 
-    size_t rate = 0;
-
-    if (Preferences::getInstance()->getResampleOnLoad()) {
+    sv_samplerate_t rate = 0;
+
+    if (Preferences::getInstance()->getFixedSampleRate() != 0) {
+        rate = Preferences::getInstance()->getFixedSampleRate();
+    } else if (Preferences::getInstance()->getResampleOnLoad()) {
         rate = m_playSource->getSourceSampleRate();
     }
 
-    WaveFileModel *newModel = new WaveFileModel(source, rate);
+    ReadOnlyWaveFileModel *newModel = new ReadOnlyWaveFileModel(source, rate);
 
     if (!newModel->isOK()) {
 	delete newModel;
         m_openingAudioFile = false;
-	return FileOpenFailed;
+        if (source.wasCancelled()) {
+            return FileOpenCancelled;
+        } else { 
+            return FileOpenFailed;
+        }
     }
 
 //    cerr << "mode = " << mode << endl;
@@ -1223,6 +1414,7 @@
     }
 
     if (mode == CreateAdditionalModel && !getMainModel()) {
+        SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl;
         mode = ReplaceSession;
     }
 
@@ -1232,7 +1424,7 @@
 
         if (!checkSaveModified()) return FileOpenCancelled;
 
-        cerr << "SV looking for template " << templateName << endl;
+        SVDEBUG << "SV looking for template " << templateName << endl;
         if (templateName != "") {
             FileOpenStatus tplStatus = openSessionTemplate(templateName);
             if (tplStatus == FileOpenCancelled) {
@@ -1246,10 +1438,12 @@
         }
 
         if (!loadedTemplate) {
+            SVDEBUG << "No template found: closing session, creating new empty document" << endl;
             closeSession();
             createDocument();
         }
 
+        SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
         mode = ReplaceMainModel;
     }
 
@@ -1363,6 +1557,8 @@
 
     currentPaneChanged(m_paneStack->getCurrentPane());
 
+    emit audioFileLoaded();
+
     return FileOpenSucceeded;
 }
 
@@ -1592,7 +1788,7 @@
 }
 
 MainWindowBase::FileOpenStatus
-MainWindowBase::openSessionFile(QString fileOrUrl)
+MainWindowBase::openSessionPath(QString fileOrUrl)
 {
     ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
     connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
@@ -1607,7 +1803,10 @@
     if (!source.isAvailable()) return FileOpenFailed;
     source.waitForData();
 
-    if (source.getExtension().toLower() != "sv") {
+    QString sessionExt = 
+        InteractiveFileFinder::getInstance()->getApplicationSessionExtension();
+
+    if (source.getExtension().toLower() != sessionExt) {
 
         RDFImporter::RDFDocumentType rdfType = 
             RDFImporter::identifyDocumentType
@@ -1638,7 +1837,7 @@
     BZipFileDevice *bzFile = 0;
     QFile *rawFile = 0;
 
-    if (source.getExtension().toLower() == "sv") {
+    if (source.getExtension().toLower() == sessionExt) {
         bzFile = new BZipFileDevice(source.getLocalFilename());
         if (!bzFile->open(QIODevice::ReadOnly)) {
             delete bzFile;
@@ -1712,6 +1911,8 @@
                                        source.getLocalFilename());
         }
 
+        emit sessionLoaded();
+
     } else {
 	setWindowTitle(QApplication::applicationName());
     }
@@ -1745,7 +1946,6 @@
 
     QXmlInputSource *inputSource = 0;
     QFile *file = 0;
-    bool isTemplate = false;
 
     file = new QFile(source.getLocalFilename());
     inputSource = new QXmlInputSource(file);
@@ -1794,6 +1994,8 @@
 	CommandHistory::getInstance()->documentSaved();
 	m_documentModified = false;
 	updateMenuStates();
+
+        emit sessionLoaded();
     }
 
     return ok ? FileOpenSucceeded : FileOpenFailed;
@@ -1825,13 +2027,15 @@
     CommandHistory::getInstance()->documentSaved();
     m_documentModified = false;
 
+    emit sessionLoaded();
+
     return status;
 }
 
 MainWindowBase::FileOpenStatus
 MainWindowBase::openLayersFromRDF(FileSource source)
 {
-    size_t rate = 0;
+    sv_samplerate_t rate = 0;
 
     SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl;
 
@@ -1977,28 +2181,44 @@
 }
 
 void
-MainWindowBase::createPlayTarget()
+MainWindowBase::createAudioIO()
 {
-    if (m_playTarget) return;
-
+    if (m_playTarget || m_audioIO) return;
+
+    if (!(m_soundOptions & WithAudioOutput)) return;
+
+    //!!! how to handle preferences
+/*    
     QSettings settings;
     settings.beginGroup("Preferences");
     QString targetName = settings.value("audio-target", "").toString();
     settings.endGroup();
-
     AudioTargetFactory *factory = AudioTargetFactory::getInstance();
 
     factory->setDefaultCallbackTarget(targetName);
-    m_playTarget = factory->createCallbackTarget(m_playSource);
-
-    if (!m_playTarget) {
+*/
+
+    if (m_soundOptions & WithAudioInput) {
+        m_audioIO = breakfastquay::AudioFactory::
+            createCallbackIO(m_recordTarget, m_playSource);
+        m_audioIO->suspend(); // start in suspended state
+        m_playSource->setSystemPlaybackTarget(m_audioIO);
+    } else {
+        m_playTarget = breakfastquay::AudioFactory::
+            createCallbackPlayTarget(m_playSource);
+        m_playTarget->suspend(); // start in suspended state
+        m_playSource->setSystemPlaybackTarget(m_playTarget);
+    }
+
+    if (!m_playTarget && !m_audioIO) {
         emit hideSplash();
 
-        if (factory->isAutoCallbackTarget(targetName)) {
+//        if (factory->isAutoCallbackTarget(targetName)) {
             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>Automatic audio device detection failed. Audio playback will not be available during this session.</p>"),
 	     QMessageBox::Ok);
+/*
         } else {
             QMessageBox::warning
                 (this, tr("Couldn't open audio device"),
@@ -2006,6 +2226,8 @@
                  .arg(factory->getCallbackTargetDescription(targetName)),
                  QMessageBox::Ok);
         }
+*/
+            return;
     }
 }
 
@@ -2077,6 +2299,7 @@
         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
 
         QTextStream out(&bzFile);
+        out.setCodec(QTextCodec::codecForName("UTF-8"));
         toXml(out, false);
         out.flush();
 
@@ -2122,6 +2345,7 @@
         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
 
         QTextStream out(&file);
+        out.setCodec(QTextCodec::codecForName("UTF-8"));
         toXml(out, true);
         out.flush();
 
@@ -2179,6 +2403,7 @@
 Pane *
 MainWindowBase::addPaneToStack()
 {
+    cerr << "MainWindowBase::addPaneToStack()" << endl;
     AddPaneCommand *command = new AddPaneCommand(this);
     CommandHistory::getInstance()->addCommand(command);
     Pane *pane = command->getPane();
@@ -2208,17 +2433,17 @@
     Model *model = getMainModel();
     if (!model) return;
     
-    size_t start = model->getStartFrame();
-    size_t end = model->getEndFrame();
+    sv_frame_t start = model->getStartFrame();
+    sv_frame_t end = model->getEndFrame();
     if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
-    size_t pixels = currentPane->width();
-
-    size_t sw = currentPane->getVerticalScaleWidth();
+    int pixels = currentPane->width();
+
+    int sw = currentPane->getVerticalScaleWidth();
     if (pixels > sw * 2) pixels -= sw * 2;
     else pixels = 1;
     if (pixels > 4) pixels -= 4;
 
-    size_t zoomLevel = (end - start) / pixels;
+    int zoomLevel = int((end - start) / pixels);
     if (zoomLevel < 1) zoomLevel = 1;
 
     currentPane->setZoomLevel(zoomLevel);
@@ -2229,7 +2454,11 @@
 MainWindowBase::zoomDefault()
 {
     Pane *currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) currentPane->setZoomLevel(1024);
+    QSettings settings;
+    settings.beginGroup("MainWindow");
+    int zoom = settings.value("zoom-default", 1024).toInt();
+    settings.endGroup();
+    if (currentPane) currentPane->setZoomLevel(zoom);
 }
 
 void
@@ -2283,7 +2512,7 @@
 void
 MainWindowBase::showMinimalOverlays()
 {
-    m_viewManager->setOverlayMode(ViewManager::MinimalOverlays);
+    m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
 }
 
 void
@@ -2358,6 +2587,22 @@
     }
 }
 
+QLabel *
+MainWindowBase::getStatusLabel() const
+{
+    if (!m_statusLabel) {
+        m_statusLabel = new QLabel();
+        statusBar()->addWidget(m_statusLabel, 1);
+    }
+
+    QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
+    foreach (QFrame *f, frames) {
+        f->setFrameStyle(QFrame::NoFrame);
+    }
+
+    return m_statusLabel;
+}
+
 void
 MainWindowBase::toggleStatusBar()
 {
@@ -2414,37 +2659,180 @@
 void
 MainWindowBase::play()
 {
-    if (m_playSource->isPlaying()) {
+    if (m_recordTarget->isRecording() || m_playSource->isPlaying()) {
         stop();
+        QAction *action = qobject_cast<QAction *>(sender());
+        if (action) action->setChecked(false);
     } else {
+        if (m_audioIO) m_audioIO->resume();
+        else if (m_playTarget) m_playTarget->resume();
         playbackFrameChanged(m_viewManager->getPlaybackFrame());
 	m_playSource->play(m_viewManager->getPlaybackFrame());
     }
 }
 
 void
+MainWindowBase::record()
+{
+    if (!(m_soundOptions & WithAudioInput)) {
+        return;
+    }
+
+    if (!m_recordTarget) {
+        //!!! report
+        return;
+    }
+
+    if (!m_audioIO) {
+        createAudioIO();
+    }
+
+    if (!m_audioIO) {
+        //!!! report
+        return;
+    }
+    
+    if (m_recordTarget->isRecording()) {
+        stop();
+        return;
+    }
+
+    QAction *action = qobject_cast<QAction *>(sender());
+    
+    if (m_audioRecordMode == RecordReplaceSession) {
+        if (!checkSaveModified()) {
+            if (action) action->setChecked(false);
+            return;
+        }
+    }
+
+    m_audioIO->resume();
+
+    WritableWaveFileModel *model = m_recordTarget->startRecording();
+    if (!model) {
+        cerr << "ERROR: MainWindowBase::record: Recording failed" << endl;
+        //!!! report
+        if (action) action->setChecked(false);
+        return;
+    }
+
+    if (!model->isOK()) {
+        m_recordTarget->stopRecording();
+        m_audioIO->suspend();
+        delete model;
+        return;
+    }
+    
+    PlayParameterRepository::getInstance()->addPlayable(model);
+
+    if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
+
+        //!!! duplication with openAudio here
+        
+        QString templateName = getDefaultSessionTemplate();
+        bool loadedTemplate = false;
+        
+        if (templateName != "") {
+            FileOpenStatus tplStatus = openSessionTemplate(templateName);
+            if (tplStatus == FileOpenCancelled) {
+                m_recordTarget->stopRecording();
+                m_audioIO->suspend();
+                PlayParameterRepository::getInstance()->removePlayable(model);
+                return;
+            }
+            if (tplStatus != FileOpenFailed) {
+                loadedTemplate = true;
+            }
+        }
+
+        if (!loadedTemplate) {
+            closeSession();
+            createDocument();
+        }
+        
+        Model *prevMain = getMainModel();
+        if (prevMain) {
+            m_playSource->removeModel(prevMain);
+            PlayParameterRepository::getInstance()->removePlayable(prevMain);
+        }
+        
+        m_document->setMainModel(model);
+        setupMenus();
+
+	if (loadedTemplate || (m_sessionFile == "")) {
+            //!!! shouldn't be dealing directly with title from here -- call a method
+	    setWindowTitle(tr("%1: %2")
+                           .arg(QApplication::applicationName())
+                           .arg(model->getLocation()));
+	    CommandHistory::getInstance()->clear();
+	    CommandHistory::getInstance()->documentSaved();
+	    m_documentModified = false;
+	} else {
+	    setWindowTitle(tr("%1: %2 [%3]")
+                           .arg(QApplication::applicationName())
+			   .arg(QFileInfo(m_sessionFile).fileName())
+			   .arg(model->getLocation()));
+	    if (m_documentModified) {
+		m_documentModified = false;
+		documentModified(); // so as to restore "(modified)" window title
+	    }
+	}
+
+    } else {
+
+        CommandHistory::getInstance()->startCompoundOperation
+            (tr("Import Recorded Audio"), true);
+
+        m_document->addImportedModel(model);
+
+        AddPaneCommand *command = new AddPaneCommand(this);
+        CommandHistory::getInstance()->addCommand(command);
+
+        Pane *pane = command->getPane();
+
+        if (m_timeRulerLayer) {
+            m_document->addLayerToView(pane, m_timeRulerLayer);
+        }
+
+        Layer *newLayer = m_document->createImportedLayer(model);
+
+        if (newLayer) {
+            m_document->addLayerToView(pane, newLayer);
+        }
+	
+        CommandHistory::getInstance()->endCompoundOperation();
+    }
+
+    updateMenuStates();
+    m_recentFiles.addFile(model->getLocation());
+    currentPaneChanged(m_paneStack->getCurrentPane());
+
+    emit audioFileLoaded();
+}
+
+void
 MainWindowBase::ffwd()
 {
     if (!getMainModel()) return;
 
-    int frame = m_viewManager->getPlaybackFrame();
+    sv_frame_t frame = m_viewManager->getPlaybackFrame();
     ++frame;
 
     Pane *pane = m_paneStack->getCurrentPane();
     Layer *layer = getSnapLayer();
-    size_t sr = getMainModel()->getSampleRate();
+    sv_samplerate_t sr = getMainModel()->getSampleRate();
 
     if (!layer) {
 
         frame = RealTime::realTime2Frame
-            (RealTime::frame2RealTime(frame, sr) + RealTime(2, 0), sr);
-        if (frame > int(getMainModel()->getEndFrame())) {
+            (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
+        if (frame > getMainModel()->getEndFrame()) {
             frame = getMainModel()->getEndFrame();
         }
 
     } else {
 
-        size_t resolution = 0;
+        int resolution = 0;
         if (pane) frame = pane->alignFromReference(frame);
         if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
                                       frame, resolution, Layer::SnapRight)) {
@@ -2457,7 +2845,7 @@
     if (frame < 0) frame = 0;
 
     if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(size_t(frame));
+        frame = m_viewManager->constrainFrameToSelection(frame);
     }
     
     m_viewManager->setPlaybackFrame(frame);
@@ -2481,7 +2869,7 @@
         stop();
     }
 
-    size_t frame = getMainModel()->getEndFrame();
+    sv_frame_t frame = getMainModel()->getEndFrame();
 
     if (m_viewManager->getPlaySelectionMode()) {
         frame = m_viewManager->constrainFrameToSelection(frame);
@@ -2499,11 +2887,9 @@
     if (!layer) { ffwd(); return; }
 
     Pane *pane = m_paneStack->getCurrentPane();
-    size_t sr = getMainModel()->getSampleRate();
-
-    int frame = m_viewManager->getPlaybackFrame();
-
-    size_t resolution = 0;
+    sv_frame_t frame = m_viewManager->getPlaybackFrame();
+
+    int resolution = 0;
     if (pane) frame = pane->alignFromReference(frame);
     if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
                                     frame, resolution, Layer::SnapRight)) {
@@ -2515,7 +2901,7 @@
     if (frame < 0) frame = 0;
 
     if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(size_t(frame));
+        frame = m_viewManager->constrainFrameToSelection(frame);
     }
     
     m_viewManager->setPlaybackFrame(frame);
@@ -2533,19 +2919,19 @@
 {
     if (!getMainModel()) return;
 
-    int frame = m_viewManager->getPlaybackFrame();
+    sv_frame_t frame = m_viewManager->getPlaybackFrame();
     if (frame > 0) --frame;
 
     Pane *pane = m_paneStack->getCurrentPane();
     Layer *layer = getSnapLayer();
-    size_t sr = getMainModel()->getSampleRate();
+    sv_samplerate_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);
+        ct = ct - RealTime::fromSeconds(0.15);
         if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
         frame = RealTime::realTime2Frame(ct, sr);
     }
@@ -2553,14 +2939,14 @@
     if (!layer) {
         
         frame = RealTime::realTime2Frame
-            (RealTime::frame2RealTime(frame, sr) - RealTime(2, 0), sr);
-        if (frame < int(getMainModel()->getStartFrame())) {
+            (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
+        if (frame < getMainModel()->getStartFrame()) {
             frame = getMainModel()->getStartFrame();
         }
 
     } else {
 
-        size_t resolution = 0;
+        int resolution = 0;
         if (pane) frame = pane->alignFromReference(frame);
         if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
                                       frame, resolution, Layer::SnapLeft)) {
@@ -2573,7 +2959,7 @@
     if (frame < 0) frame = 0;
 
     if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(size_t(frame));
+        frame = m_viewManager->constrainFrameToSelection(frame);
     }
 
     m_viewManager->setPlaybackFrame(frame);
@@ -2584,7 +2970,7 @@
 {
     if (!getMainModel()) return;
 
-    size_t frame = getMainModel()->getStartFrame();
+    sv_frame_t frame = getMainModel()->getStartFrame();
 
     if (m_viewManager->getPlaySelectionMode()) {
         frame = m_viewManager->constrainFrameToSelection(frame);
@@ -2602,11 +2988,9 @@
     if (!layer) { rewind(); return; }
 
     Pane *pane = m_paneStack->getCurrentPane();
-    size_t sr = getMainModel()->getSampleRate();
-
-    int frame = m_viewManager->getPlaybackFrame();
-
-    size_t resolution = 0;
+    sv_frame_t frame = m_viewManager->getPlaybackFrame();
+
+    int resolution = 0;
     if (pane) frame = pane->alignFromReference(frame);
     if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
                                     frame, resolution, Layer::SnapLeft)) {
@@ -2618,7 +3002,7 @@
     if (frame < 0) frame = 0;
 
     if (m_viewManager->getPlaySelectionMode()) {
-        frame = m_viewManager->constrainFrameToSelection(size_t(frame));
+        frame = m_viewManager->constrainFrameToSelection(frame);
     }
     
     m_viewManager->setPlaybackFrame(frame);
@@ -2654,13 +3038,20 @@
 void
 MainWindowBase::stop()
 {
+    if (m_recordTarget->isRecording()) {
+        m_recordTarget->stopRecording();
+    }
+        
     m_playSource->stop();
 
+    if (m_audioIO) m_audioIO->suspend();
+    else if (m_playTarget) m_playTarget->suspend();
+    
     if (m_paneStack && m_paneStack->getCurrentPane()) {
         updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
     } else {
         m_myStatusMessage = "";
-        statusBar()->showMessage("");
+        getStatusLabel()->setText("");
     }
 }
 
@@ -2713,6 +3104,7 @@
 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
     m_mw(mw),
     m_pane(pane),
+    m_prevCurrentPane(0),
     m_added(true)
 {
 }
@@ -2830,24 +3222,24 @@
 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
 {
     connect(m_viewManager,
-            SIGNAL(globalCentreFrameChanged(unsigned long)),
+            SIGNAL(globalCentreFrameChanged(sv_frame_t)),
             dialog,
-            SLOT(userScrolledToFrame(unsigned long)));
+            SLOT(userScrolledToFrame(sv_frame_t)));
 
     connect(m_viewManager,
-            SIGNAL(playbackFrameChanged(unsigned long)),
+            SIGNAL(playbackFrameChanged(sv_frame_t)),
             dialog,
-            SLOT(playbackScrolledToFrame(unsigned long)));
+            SLOT(playbackScrolledToFrame(sv_frame_t)));
 
     connect(dialog,
-            SIGNAL(scrollToFrame(unsigned long)),
+            SIGNAL(scrollToFrame(sv_frame_t)),
             m_viewManager,
-            SLOT(setGlobalCentreFrame(unsigned long)));
+            SLOT(setGlobalCentreFrame(sv_frame_t)));
 
     connect(dialog,
-            SIGNAL(scrollToFrame(unsigned long)),
+            SIGNAL(scrollToFrame(sv_frame_t)),
             m_viewManager,
-            SLOT(setPlaybackFrame(unsigned long)));
+            SLOT(setPlaybackFrame(sv_frame_t)));
 }    
 
 void
@@ -2889,53 +3281,75 @@
 void
 MainWindowBase::previousLayer()
 {
-    //!!! Not right -- pane lists layers in stacking order
-
     if (!m_paneStack) return;
 
     Pane *currentPane = m_paneStack->getCurrentPane();
     if (!currentPane) return;
 
+    int count = currentPane->getLayerCount();
+    if (count == 0) return;
+
     Layer *currentLayer = currentPane->getSelectedLayer();
-    if (!currentLayer) return;
-
-    for (int i = 0; i < currentPane->getLayerCount(); ++i) {
-        if (currentPane->getLayer(i) == currentLayer) {
-            if (i == 0) return;
-            m_paneStack->setCurrentLayer(currentPane,
-                                         currentPane->getLayer(i-1));
-            updateMenuStates();
-            return;
+
+    if (!currentLayer) {
+        // The pane itself is current
+        m_paneStack->setCurrentLayer
+            (currentPane, currentPane->getFixedOrderLayer(count-1));
+    } else {
+        for (int i = 0; i < count; ++i) {
+            if (currentPane->getFixedOrderLayer(i) == currentLayer) {
+                if (i == 0) {
+                    m_paneStack->setCurrentLayer
+                        (currentPane, 0); // pane
+                } else {
+                    m_paneStack->setCurrentLayer
+                        (currentPane, currentPane->getFixedOrderLayer(i-1));
+                }
+                break;
+            }
         }
     }
+
+    updateMenuStates();
 }
 
 void
 MainWindowBase::nextLayer()
 {
-    //!!! Not right -- pane lists layers in stacking order
-
     if (!m_paneStack) return;
 
     Pane *currentPane = m_paneStack->getCurrentPane();
     if (!currentPane) return;
 
+    int count = currentPane->getLayerCount();
+    if (count == 0) return;
+
     Layer *currentLayer = currentPane->getSelectedLayer();
-    if (!currentLayer) return;
-
-    for (int i = 0; i < currentPane->getLayerCount(); ++i) {
-        if (currentPane->getLayer(i) == currentLayer) {
-            if (i == currentPane->getLayerCount()-1) return;
-            m_paneStack->setCurrentLayer(currentPane,
-                                         currentPane->getLayer(i+1));
-            updateMenuStates();
-            return;
+
+    if (!currentLayer) {
+        // The pane itself is current
+        m_paneStack->setCurrentLayer
+            (currentPane, currentPane->getFixedOrderLayer(0));
+    } else {
+        for (int i = 0; i < count; ++i) {
+            if (currentPane->getFixedOrderLayer(i) == currentLayer) {
+                if (i == currentPane->getLayerCount()-1) {
+                    m_paneStack->setCurrentLayer
+                        (currentPane, 0); // pane
+                } else {
+                    m_paneStack->setCurrentLayer
+                        (currentPane, currentPane->getFixedOrderLayer(i+1));
+                }
+                break;
+            }
         }
     }
+
+    updateMenuStates();
 }
 
 void
-MainWindowBase::playbackFrameChanged(unsigned long frame)
+MainWindowBase::playbackFrameChanged(sv_frame_t frame)
 {
     if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
 
@@ -2967,11 +3381,22 @@
     m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
         .arg(nowStr).arg(thenStr).arg(remainingStr);
 
-    statusBar()->showMessage(m_myStatusMessage);
+    getStatusLabel()->setText(m_myStatusMessage);
 }
 
 void
-MainWindowBase::globalCentreFrameChanged(unsigned long )
+MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
+{
+    RealTime duration = RealTime::frame2RealTime(frame, rate);
+    QString durStr = duration.toSecText().c_str();
+    
+    m_myStatusMessage = tr("Recording: %1").arg(durStr);
+
+    getStatusLabel()->setText(m_myStatusMessage);
+}
+
+void
+MainWindowBase::globalCentreFrameChanged(sv_frame_t )
 {
     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
     Pane *p = 0;
@@ -2981,7 +3406,7 @@
 }
 
 void
-MainWindowBase::viewCentreFrameChanged(View *v, unsigned long frame)
+MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
 {
 //    SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
 
@@ -2998,7 +3423,7 @@
 }
 
 void
-MainWindowBase::viewZoomLevelChanged(View *v, unsigned long , bool )
+MainWindowBase::viewZoomLevelChanged(View *v, int , bool )
 {
     if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
     Pane *p = 0;
@@ -3091,6 +3516,7 @@
 MainWindowBase::modelAdded(Model *model)
 {
 //    SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
+	std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl;
     m_playSource->addModel(model);
 }
 
@@ -3100,7 +3526,10 @@
 //    SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
     updateDescriptionLabel();
     if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
-    if (model && !m_playTarget && m_audioOutput) createPlayTarget();
+    if (model && !(m_playTarget || m_audioIO) &&
+        (m_soundOptions & WithAudioOutput)) {
+        createAudioIO();
+    }
 }
 
 void
@@ -3111,7 +3540,6 @@
         m_viewManager->setPlaybackModel(0);
     }
     m_playSource->removeModel(model);
-    FFTDataServer::modelAboutToBeDeleted(model);
 }
 
 void
@@ -3172,17 +3600,25 @@
 {
     Pane *currentPane = 0;
     if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
-    if (currentPane) updateVisibleRangeDisplay(currentPane);
+    if (currentPane) {
+        //cerr << "JTEST: mouse event on selection pane" << endl;
+        updateVisibleRangeDisplay(currentPane);
+    }
 }
 
 void
 MainWindowBase::contextHelpChanged(const QString &s)
 {
+    QLabel *lab = getStatusLabel();
+
     if (s == "" && m_myStatusMessage != "") {
-        statusBar()->showMessage(m_myStatusMessage);
+        if (lab->text() != m_myStatusMessage) {
+            lab->setText(m_myStatusMessage);
+        }
         return;
     }
-    statusBar()->showMessage(s);
+
+    lab->setText(s);
 }
 
 void
@@ -3200,15 +3636,12 @@
     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);
-
+    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);
@@ -3222,7 +3655,32 @@
     }
 #endif
 #endif
+}
+
+void
+MainWindowBase::openLocalFolder(QString path)
+{
+    QDir d(path);
+    if (d.exists()) {
+        QStringList args;
+        QString path = d.canonicalPath();
+#if defined Q_OS_WIN32
+        // Although the Win32 API is quite happy to have
+        // forward slashes as directory separators, Windows
+        // Explorer is not
+        path = path.replace('/', '\\');
+        args << path;
+        QProcess::execute("c:/windows/explorer.exe", args);
+#else
+        args << path;
+        QProcess::execute(
+#if defined Q_OS_MAC
+            "/usr/bin/open",
+#else
+            "/usr/bin/xdg-open",
 #endif
+            args);
+#endif
+    }
 }
 
-    
--- a/framework/MainWindowBase.h	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/MainWindowBase.h	Wed Apr 20 12:06:28 2016 +0100
@@ -46,7 +46,7 @@
 class WaveformLayer;
 class WaveFileModel;
 class AudioCallbackPlaySource;
-class AudioCallbackPlayTarget;
+class AudioRecordTarget;
 class CommandHistory;
 class QMenu;
 class AudioDial;
@@ -60,6 +60,13 @@
 class KeyReference;
 class Labeller;
 class ModelDataTableDialog;
+class QSignalMapper;
+class QShortcut;
+
+namespace breakfastquay {
+class SystemPlaybackTarget;
+class SystemAudioIO;
+}
 
 /**
  * The base class for the SV main window.  This includes everything to
@@ -75,7 +82,15 @@
     Q_OBJECT
 
 public:
-    MainWindowBase(bool withAudioOutput, bool withOSCSupport, bool withMIDIInput);
+    enum SoundOption {
+        WithAudioOutput = 0x01,
+        WithAudioInput  = 0x02,
+        WithMIDIInput   = 0x04,
+        WithEverything  = 0xff
+    };
+    typedef int SoundOptions;
+    
+    MainWindowBase(SoundOptions options = WithEverything);
     virtual ~MainWindowBase();
     
     enum AudioFileOpenMode {
@@ -93,16 +108,20 @@
         FileOpenWrongMode // attempted to open layer when no main model present
     };
 
-    virtual FileOpenStatus open(QString fileOrUrl, AudioFileOpenMode = AskUser);
+    enum AudioRecordMode {
+        RecordReplaceSession,
+        RecordCreateAdditionalModel
+    };
+    
     virtual FileOpenStatus open(FileSource source, AudioFileOpenMode = AskUser);
-    
+    virtual FileOpenStatus openPath(QString fileOrUrl, AudioFileOpenMode = AskUser);
     virtual FileOpenStatus openAudio(FileSource source, AudioFileOpenMode = AskUser, QString templateName = "");
     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 FileOpenStatus openSessionPath(QString fileOrUrl);
     virtual FileOpenStatus openSessionTemplate(QString templateName);
     virtual FileOpenStatus openSessionTemplate(FileSource source);
 
@@ -110,8 +129,16 @@
     virtual bool saveSessionTemplate(QString path);
 
     /// Implementation of FrameTimer interface method
-    virtual unsigned long getFrame() const;
+    virtual sv_frame_t getFrame() const;
 
+    void setDefaultFfwdRwdStep(RealTime step) {
+        m_defaultFfwdRwdStep = step;
+    }
+
+    void setAudioRecordMode(AudioRecordMode mode) {
+        m_audioRecordMode = mode;
+    }
+    
 signals:
     // Used to toggle the availability of menu actions
     void canAddPane(bool);
@@ -141,6 +168,7 @@
     void canZoom(bool);
     void canScroll(bool);
     void canPlay(bool);
+    void canRecord(bool);
     void canFfwd(bool);
     void canRewind(bool);
     void canPlaySelection(bool);
@@ -152,7 +180,11 @@
     void canSelectPreviousLayer(bool);
     void canSelectNextLayer(bool);
     void canSave(bool);
+    void canSaveAs(bool);
     void hideSplash();
+    void hideSplash(QWidget *);
+    void sessionLoaded();
+    void audioFileLoaded();
     void replacedDocument();
     void activity(QString);
 
@@ -187,6 +219,7 @@
     virtual void ffwdEnd();
     virtual void rewind();
     virtual void rewindStart();
+    virtual void record();
     virtual void stop();
 
     virtual void ffwdSimilar();
@@ -205,15 +238,16 @@
     virtual void playSelectionToggled();
     virtual void playSoloToggled();
 
-    virtual void sampleRateMismatch(size_t, size_t, bool) = 0;
+    virtual void sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool) = 0;
     virtual void audioOverloadPluginDisabled() = 0;
     virtual void audioTimeStretchMultiChannelDisabled() = 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 playbackFrameChanged(sv_frame_t);
+    virtual void globalCentreFrameChanged(sv_frame_t);
+    virtual void viewCentreFrameChanged(View *, sv_frame_t);
+    virtual void viewZoomLevelChanged(View *, int, bool);
     virtual void outputLevelsChanged(float, float) = 0;
+    virtual void recordDurationChanged(sv_frame_t, sv_samplerate_t);
 
     virtual void currentPaneChanged(Pane *);
     virtual void currentLayerChanged(Pane *, Layer *);
@@ -228,14 +262,14 @@
     virtual void copy();
     virtual void paste();
     virtual void pasteAtPlaybackPosition();
-    virtual void pasteRelative(int offset);
+    virtual void pasteRelative(sv_frame_t offset);
     virtual void deleteSelected();
 
     virtual void insertInstant();
-    virtual void insertInstantAt(size_t);
+    virtual void insertInstantAt(sv_frame_t);
     virtual void insertInstantsAtBoundaries();
     virtual void insertItemAtSelection();
-    virtual void insertItemAt(size_t, size_t);
+    virtual void insertItemAt(sv_frame_t, sv_frame_t);
     virtual void renumberInstants();
 
     virtual void documentModified();
@@ -280,8 +314,12 @@
 
     virtual void closeSession() = 0;
 
+    virtual void emitHideSplash();
+
     virtual void newerVersionAvailable(QString) { }
 
+    virtual void menuActionMapperInvoked(QObject *);
+
 protected:
     QString                  m_sessionFile;
     QString                  m_audioFile;
@@ -291,9 +329,12 @@
     ViewManager             *m_viewManager;
     Layer                   *m_timeRulerLayer;
 
-    bool                     m_audioOutput;
+    SoundOptions             m_soundOptions;
+
     AudioCallbackPlaySource *m_playSource;
-    AudioCallbackPlayTarget *m_playTarget;
+    AudioRecordTarget       *m_recordTarget;
+    breakfastquay::SystemPlaybackTarget *m_playTarget; // only one of this...
+    breakfastquay::SystemAudioIO *m_audioIO;           // ... and this exists
 
     class OSCQueueStarter : public QThread
     {
@@ -309,6 +350,7 @@
 
     OSCQueue                *m_oscQueue;
     OSCQueueStarter         *m_oscQueueStarter;
+    void startOSCQueue();
 
     MIDIInput               *m_midiInput;
 
@@ -326,6 +368,13 @@
 
     bool                     m_initialDarkBackground;
 
+    RealTime                 m_defaultFfwdRwdStep;
+
+    AudioRecordMode          m_audioRecordMode;
+
+    mutable QLabel *m_statusLabel;
+    QLabel *getStatusLabel() const;
+
     WaveFileModel *getMainModel();
     const WaveFileModel *getMainModel() const;
     void createDocument();
@@ -350,8 +399,8 @@
 	virtual void setWindowSize(int width, int height) {
 	    m_mw->resizeConstrained(QSize(width, height));
 	}
-	virtual void addSelection(int start, int end) {
-	    m_mw->m_viewManager->addSelection(Selection(start, end));
+	virtual void addSelection(sv_frame_t start, sv_frame_t end) {
+	    m_mw->m_viewManager->addSelectionQuietly(Selection(start, end));
 	}
     protected:
 	MainWindowBase *m_mw;
@@ -402,13 +451,23 @@
     virtual QString getDefaultSessionTemplate() const;
     virtual void setDefaultSessionTemplate(QString);
 
-    virtual void createPlayTarget();
+    virtual void createAudioIO();
     virtual void openHelpUrl(QString url);
+    virtual void openLocalFolder(QString path);
 
     virtual void setupMenus() = 0;
     virtual void updateVisibleRangeDisplay(Pane *p) const = 0;
     virtual void updatePositionStatusDisplays() const = 0;
 
+    // Call this after setting up the menu bar, to fix up single-key
+    // shortcuts on OS/X
+    virtual void finaliseMenus();
+    virtual void finaliseMenu(QMenu *);
+
+    // Only used on OS/X to work around a Qt/Cocoa bug, see finaliseMenus
+    QSignalMapper *m_menuShortcutMapper;
+    QList<QShortcut *> m_appShortcuts;
+
     virtual bool shouldCreateNewSessionForRDFAudio(bool *) { return true; }
 
     virtual void connectLayerEditDialog(ModelDataTableDialog *dialog);
--- a/framework/SVFileReader.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/SVFileReader.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -26,11 +26,12 @@
 
 #include "data/fileio/FileFinder.h"
 
-#include "data/model/WaveFileModel.h"
+#include "data/model/ReadOnlyWaveFileModel.h"
 #include "data/model/EditableDenseThreeDimensionalModel.h"
 #include "data/model/SparseOneDimensionalModel.h"
 #include "data/model/SparseTimeValueModel.h"
 #include "data/model/NoteModel.h"
+#include "data/model/FlexiNoteModel.h"
 #include "data/model/RegionModel.h"
 #include "data/model/TextModel.h"
 #include "data/model/ImageModel.h"
@@ -57,15 +58,20 @@
     m_paneCallback(callback),
     m_location(location),
     m_currentPane(0),
+    m_currentLayer(0),
     m_currentDataset(0),
     m_currentDerivedModel(0),
     m_currentDerivedModelId(-1),
     m_currentPlayParameters(0),
     m_currentTransformSource(0),
+    m_currentTransformChannel(0),
+    m_currentTransformIsNewStyle(true),
     m_datasetSeparator(" "),
     m_inRow(false),
     m_inLayer(false),
     m_inView(false),
+    m_inData(false),
+    m_inSelections(false),
     m_rowNumber(0),
     m_ok(false)
 {
@@ -299,7 +305,7 @@
     } else if (name == "derivation") {
 
         if (!m_currentDerivedModel) {
-            if (m_currentDerivedModel < 0) {
+            if (m_currentDerivedModelId < 0) {
                 cerr << "WARNING: SV-XML: Bad derivation output model id "
                           << m_currentDerivedModelId << endl;
             } else if (haveModel(m_currentDerivedModelId)) {
@@ -325,7 +331,7 @@
                 }                    
             }
         } else {
-            m_document->addDerivedModel
+            m_document->addAlreadyDerivedModel
                 (m_currentTransform,
                  ModelTransformer::Input(m_currentTransformSource,
                                          m_currentTransformChannel),
@@ -444,10 +450,10 @@
 
     SVDEBUG << "SVFileReader::readModel: model name \"" << name << "\"" << endl;
 
-    READ_MANDATORY(int, sampleRate, toInt);
+    READ_MANDATORY(double, sampleRate, toDouble);
 
     QString type = attributes.value("type").trimmed();
-    bool mainModel = (attributes.value("mainModel").trimmed() == "true");
+    bool isMainModel = (attributes.value("mainModel").trimmed() == "true");
 
     if (type == "wavefile") {
 	
@@ -472,15 +478,18 @@
 
             file.waitForData();
 
-            size_t rate = 0;
+            sv_samplerate_t rate = sampleRate;
 
-            if (!mainModel &&
-                Preferences::getInstance()->getResampleOnLoad()) {
+            if (Preferences::getInstance()->getFixedSampleRate() != 0) {
+                rate = Preferences::getInstance()->getFixedSampleRate();
+            } else if (rate == 0 &&
+                       !isMainModel &&
+                       Preferences::getInstance()->getResampleOnLoad()) {
                 WaveFileModel *mm = m_document->getMainModel();
                 if (mm) rate = mm->getSampleRate();
             }
 
-            model = new WaveFileModel(file, rate);
+            model = new ReadOnlyWaveFileModel(file, rate);
             if (!model->isOK()) {
                 delete model;
                 model = 0;
@@ -491,7 +500,7 @@
 
         model->setObjectName(name);
 	m_models[id] = model;
-	if (mainModel) {
+	if (isMainModel) {
 	    m_document->setMainModel(model);
 	    m_addedModels.insert(model);
 	}
@@ -624,6 +633,19 @@
                     model->setScaleUnits(units);
                     model->setObjectName(name);
                     m_models[id] = model;
+                } else if (attributes.value("subtype") == "flexinote") {
+                    FlexiNoteModel *model;
+                    if (haveMinMax) {
+                        model = new FlexiNoteModel
+                            (sampleRate, resolution, minimum, maximum, notifyOnAdd);
+                    } else {
+                        model = new FlexiNoteModel
+                            (sampleRate, resolution, notifyOnAdd);
+                    }
+                    model->setValueQuantization(valueQuantization);
+                    model->setScaleUnits(units);
+                    model->setObjectName(name);
+                    m_models[id] = model;
                 } else {
                     // note models written out by SV 1.3 and earlier
                     // have no subtype, so we can't test that
@@ -726,6 +748,8 @@
 
     m_currentPane = m_paneCallback.addPane();
 
+    cerr << "SVFileReader::addPane: pane is " << m_currentPane << endl;
+
     if (!m_currentPane) {
 	cerr << "WARNING: SV-XML: Internal error: Failed to add pane!"
 		  << endl;
@@ -738,8 +762,8 @@
 
     // The view properties first
 
-    READ_MANDATORY(size_t, centre, toUInt);
-    READ_MANDATORY(size_t, zoom, toUInt);
+    READ_MANDATORY(int, centre, toInt);
+    READ_MANDATORY(int, zoom, toInt);
     READ_MANDATORY(int, followPan, toInt);
     READ_MANDATORY(int, followZoom, toInt);
     QString tracking = attributes.value("tracking");
@@ -748,7 +772,8 @@
     view->setFollowGlobalPan(followPan);
     view->setFollowGlobalZoom(followZoom);
     view->setPlaybackFollow(tracking == "scroll" ? PlaybackScrollContinuous :
-			    tracking == "page" ? PlaybackScrollPage
+			    tracking == "page" ? PlaybackScrollPageWithCentre :
+			    tracking == "daw" ? PlaybackScrollPage
 			    : PlaybackIgnore);
 
     // Then set these values
@@ -861,13 +886,20 @@
 	    } else {
 		cerr << "WARNING: SV-XML: Unknown model id " << modelId
 			  << " in layer definition" << endl;
-	    }
+                if (!layer->canExistWithoutModel()) {
+                    // Don't add a layer with an unknown model id
+                    // unless it explicitly supports this state
+                    m_document->deleteLayer(layer);
+                    m_layers[id] = layer = 0;
+                    return false;
+                }
+            }
 	}
 
-	layer->setProperties(attributes);
+        if (layer) layer->setProperties(attributes);
     }
 
-    if (!m_inData && m_currentPane) {
+    if (!m_inData && m_currentPane && layer) {
 
         QString visible = attributes.value("visible");
         bool dormant = (visible == "false");
@@ -887,7 +919,7 @@
     }
 
     m_currentLayer = layer;
-    m_inLayer = true;
+    m_inLayer = (layer != 0);
 
     return true;
 }
@@ -932,6 +964,7 @@
 
     case 3:
 	if (dynamic_cast<NoteModel *>(model)) good = true;
+	else if (dynamic_cast<FlexiNoteModel *>(model)) good = true;
 	else if (dynamic_cast<RegionModel *>(model)) good = true;
 	else if (dynamic_cast<EditableDenseThreeDimensionalModel *>(model)) {
 	    m_datasetSeparator = attributes.value("separator");
@@ -987,8 +1020,8 @@
 //        cerr << "Current dataset is a note model" << endl;
 	float value = 0.0;
 	value = attributes.value("value").trimmed().toFloat(&ok);
-	size_t duration = 0;
-	duration = attributes.value("duration").trimmed().toUInt(&ok);
+	int duration = 0;
+	duration = attributes.value("duration").trimmed().toInt(&ok);
 	QString label = attributes.value("label");
         float level = attributes.value("level").trimmed().toFloat(&ok);
         if (!ok) { // level is optional
@@ -999,14 +1032,32 @@
 	return ok;
     }
 
+    FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(m_currentDataset);
+
+    if (fnm) {
+//        cerr << "Current dataset is a flexinote model" << endl;
+	float value = 0.0;
+	value = attributes.value("value").trimmed().toFloat(&ok);
+	int duration = 0;
+	duration = attributes.value("duration").trimmed().toInt(&ok);
+	QString label = attributes.value("label");
+        float level = attributes.value("level").trimmed().toFloat(&ok);
+        if (!ok) { // level is optional
+            level = 1.f;
+            ok = true;
+        }
+	fnm->addPoint(FlexiNoteModel::Point(frame, value, duration, level, label));
+	return ok;
+    }
+
     RegionModel *rm = dynamic_cast<RegionModel *>(m_currentDataset);
 
     if (rm) {
-//        cerr << "Current dataset is a note model" << endl;
+//        cerr << "Current dataset is a region model" << endl;
 	float value = 0.0;
 	value = attributes.value("value").trimmed().toFloat(&ok);
-	size_t duration = 0;
-	duration = attributes.value("duration").trimmed().toUInt(&ok);
+	int duration = 0;
+	duration = attributes.value("duration").trimmed().toInt(&ok);
 	QString label = attributes.value("label");
 	rm->addPoint(RegionModel::Point(frame, value, duration, label));
 	return ok;
@@ -1115,7 +1166,7 @@
 
 	for (QStringList::iterator i = data.begin(); i != data.end(); ++i) {
 
-	    if (values.size() == dtdm->getHeight()) {
+	    if (int(values.size()) == dtdm->getHeight()) {
 		if (!warned) {
 		    cerr << "WARNING: SV-XML: Too many y-bins in 3-D dataset row "
 			      << m_rowNumber << endl;
@@ -1210,8 +1261,8 @@
     QString startFrameStr = attributes.value("startFrame");
     QString durationStr = attributes.value("duration");
 
-    size_t startFrame = 0;
-    size_t duration = 0;
+    int startFrame = 0;
+    int duration = 0;
 
     if (startFrameStr != "") {
         startFrame = startFrameStr.trimmed().toInt(&ok);
@@ -1271,8 +1322,8 @@
         float gain = attributes.value("gain").toFloat(&ok);
         if (ok) parameters->setPlayGain(gain);
         
-        QString pluginId = attributes.value("pluginId");
-        if (pluginId != "") parameters->setPlayPluginId(pluginId);
+        QString clipId = attributes.value("clipId");
+        if (clipId != "") parameters->setPlayClipId(clipId);
         
         m_currentPlayParameters = parameters;
 
@@ -1291,17 +1342,26 @@
 bool
 SVFileReader::readPlugin(const QXmlAttributes &attributes)
 {
-    if (m_currentDerivedModelId < 0 && !m_currentPlayParameters) {
+    if (m_currentDerivedModelId >= 0) {
+        return readPluginForTransform(attributes);
+    } else if (m_currentPlayParameters) {
+        return readPluginForPlayback(attributes);
+    } else {
         cerr << "WARNING: SV-XML: Plugin found outside derivation or play parameters" << endl;
         return false;
     }
+}
 
-    if (!m_currentPlayParameters && m_currentTransformIsNewStyle) {
+bool
+SVFileReader::readPluginForTransform(const QXmlAttributes &attributes)
+{
+    if (m_currentTransformIsNewStyle) {
+        // Not needed, we have the transform element instead
         return true;
     }
 
     QString configurationXml = "<plugin";
-    
+
     for (int i = 0; i < attributes.length(); ++i) {
         configurationXml += QString(" %1=\"%2\"")
             .arg(attributes.qName(i))
@@ -1310,12 +1370,21 @@
 
     configurationXml += "/>";
 
-    if (m_currentPlayParameters) {
-        m_currentPlayParameters->setPlayPluginConfiguration(configurationXml);
-    } else {
-        TransformFactory::getInstance()->
-            setParametersFromPluginConfigurationXml(m_currentTransform,
-                                                    configurationXml);
+    TransformFactory::getInstance()->
+        setParametersFromPluginConfigurationXml(m_currentTransform,
+                                                configurationXml);
+    return true;
+}
+
+bool
+SVFileReader::readPluginForPlayback(const QXmlAttributes &attributes)
+{
+    // Obsolete but supported for compatibility
+
+    QString ident = attributes.value("identifier");
+    if (ident == "sample_player") {
+        QString clipId = attributes.value("program");
+        if (clipId != "") m_currentPlayParameters->setPlayClipId(clipId);
     }
 
     return true;
--- a/framework/SVFileReader.h	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/SVFileReader.h	Wed Apr 20 12:06:28 2016 +0100
@@ -34,7 +34,7 @@
     virtual ~SVFileReaderPaneCallback();
     virtual Pane *addPane() = 0;
     virtual void setWindowSize(int width, int height) = 0;
-    virtual void addSelection(int start, int end) = 0;
+    virtual void addSelection(sv_frame_t start, sv_frame_t end) = 0;
 };
 
 /**
@@ -222,6 +222,8 @@
     bool readDerivation(const QXmlAttributes &);
     bool readPlayParameters(const QXmlAttributes &);
     bool readPlugin(const QXmlAttributes &);
+    bool readPluginForTransform(const QXmlAttributes &);
+    bool readPluginForPlayback(const QXmlAttributes &);
     bool readTransform(const QXmlAttributes &);
     bool readParameter(const QXmlAttributes &);
     bool readSelection(const QXmlAttributes &);
--- a/framework/TransformUserConfigurator.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/TransformUserConfigurator.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -30,6 +30,14 @@
 
 #include <typeinfo>
 
+static QWidget *parentWidget = 0;
+
+void
+TransformUserConfigurator::setParentWidget(QWidget *w)
+{
+    parentWidget = w;
+}
+
 bool
 TransformUserConfigurator::getChannelRange(TransformId identifier,
 					   Vamp::PluginBase *plugin,
@@ -38,8 +46,8 @@
     if (plugin && plugin->getType() == "Feature Extraction Plugin") {
 	Vamp::Plugin *vp = static_cast<Vamp::Plugin *>(plugin);
 	SVDEBUG << "TransformUserConfigurator::getChannelRange: is a VP" << endl;
-        minChannels = vp->getMinChannelCount();
-        maxChannels = vp->getMaxChannelCount();
+        minChannels = int(vp->getMinChannelCount());
+        maxChannels = int(vp->getMaxChannelCount());
         return true;
     } else {
 	SVDEBUG << "TransformUserConfigurator::getChannelRange: is not a VP" << endl;
@@ -54,8 +62,8 @@
 				     Vamp::PluginBase *plugin,
                                      Model *&inputModel,
 				     AudioPlaySource *source,
-				     size_t startFrame,
-				     size_t duration,
+				     sv_frame_t startFrame,
+				     sv_frame_t duration,
 				     const QMap<QString, Model *> &modelMap,
 				     QStringList candidateModelNames,
 				     QString defaultModelName)
@@ -142,7 +150,8 @@
 
     int defaultChannel = -1; //!!! no longer saved! [was context.channel]
 
-    PluginParameterDialog *dialog = new PluginParameterDialog(plugin);
+    PluginParameterDialog *dialog = new PluginParameterDialog
+        (plugin, parentWidget);
 
     dialog->setMoreInfoUrl(TransformFactory::getInstance()->
 			   getTransformInfoUrl(transform.getIdentifier()));
@@ -201,7 +210,7 @@
 	}
     }
 
-    size_t stepSize = 0, blockSize = 0;
+    int stepSize = 0, blockSize = 0;
     WindowType windowType = HanningWindow;
 
     dialog->getProcessingParameters(stepSize,
--- a/framework/TransformUserConfigurator.h	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/TransformUserConfigurator.h	Wed Apr 20 12:06:28 2016 +0100
@@ -27,12 +27,14 @@
                            Vamp::PluginBase *plugin,
                            Model *&inputModel,
 			   AudioPlaySource *source,
-			   size_t startFrame,
-			   size_t duration,
+			   sv_frame_t startFrame,
+			   sv_frame_t duration,
 			   const QMap<QString, Model *> &modelMap,
                            QStringList candidateModelNames,
                            QString defaultModelName);
 
+    static void setParentWidget(QWidget *);
+
 private:
     bool getChannelRange(TransformId identifier,
                          Vamp::PluginBase *plugin, int &min, int &max);
--- a/framework/VersionTester.cpp	Tue Jul 14 15:04:45 2015 +0100
+++ b/framework/VersionTester.cpp	Wed Apr 20 12:06:28 2016 +0100
@@ -60,17 +60,21 @@
     int be = blist.size();
     int e = std::max(ae, be);
     for (int i = 0; i < e; ++i) {
-    int an = 0, bn = 0;
-    if (i < ae) {
-        an = alist[i].toInt();
-        if (an == 0) an = -1; // non-numeric field -> "-pre1" etc
-    }
-    if (i < be) {
-        bn = blist[i].toInt();
-        if (bn == 0) bn = -1;
-    }
-    if (an < bn) return false;
-    if (an > bn) return true;
+        int an = 0, bn = 0;
+        if (i < ae) {
+            an = alist[i].toInt();
+            if (an == 0 && alist[i] != "0") {
+                an = -1; // non-numeric field -> "-pre1" etc
+            }
+        }
+        if (i < be) {
+            bn = blist[i].toInt();
+            if (bn == 0 && blist[i] != "0") {
+                bn = -1;
+            }
+        }
+        if (an < bn) return false;
+        if (an > bn) return true;
     }
     return false;
 }
@@ -103,8 +107,9 @@
     if (lines.empty()) return;
 
     QString latestVersion = lines[0];
-    SVDEBUG << "Comparing current version \"" << m_myVersion              << "\" with latest version \"" << latestVersion	      << "\"" << endl;
+    cerr << "Comparing current version \"" << m_myVersion << "\" with latest version \"" << latestVersion << "\"" << endl;
     if (isVersionNewerThan(latestVersion, m_myVersion)) {
+        cerr << "Latest version \"" << latestVersion << "\" is newer than current version \"" << m_myVersion << "\"" << endl;
         emit newerVersionAvailable(latestVersion);
     }
 }
--- a/framework/framework.pro	Tue Jul 14 15:04:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-TEMPLATE = lib
-
-SV_UNIT_PACKAGES = vamp vamp-hostsdk # required because we use transform headers
-load(../prf/sv.prf)
-
-CONFIG += sv staticlib qt thread warn_on stl rtti exceptions
-QT += xml network
-
-TARGET = svframework
-
-DEPENDPATH += ..
-INCLUDEPATH += . ..
-OBJECTS_DIR = tmp_obj
-MOC_DIR = tmp_moc
-
-HEADERS += Document.h \
-           MainWindowBase.h \
-           SVFileReader.h \
-           VersionTester.h
-
-SOURCES += Document.cpp \
-           MainWindowBase.cpp \
-           SVFileReader.cpp \
-           VersionTester.cpp
-
--- a/svapp.pro	Tue Jul 14 15:04:45 2015 +0100
+++ b/svapp.pro	Wed Apr 20 12:06:28 2016 +0100
@@ -1,50 +1,61 @@
 
 TEMPLATE = lib
 
+INCLUDEPATH += ../vamp-plugin-sdk
+DEFINES += HAVE_VAMP HAVE_VAMPHOSTSDK
+
 exists(config.pri) {
     include(config.pri)
 }
-win* {
-    !exists(config.pri) {
-        DEFINES += HAVE_PORTAUDIO_2_0
+!exists(config.pri) {
+
+    CONFIG += release
+    DEFINES += NDEBUG BUILD_RELEASE NO_TIMING
+
+    win32-g++ {
+        INCLUDEPATH += ../sv-dependency-builds/win32-mingw/include
+        LIBS += -L../sv-dependency-builds/win32-mingw/lib
+    }
+    win32-msvc* {
+        INCLUDEPATH += ../sv-dependency-builds/win32-msvc/include
+        LIBS += -L../sv-dependency-builds/win32-msvc/lib
+    }
+    macx* {
+        INCLUDEPATH += ../sv-dependency-builds/osx/include
+        LIBS += -L../sv-dependency-builds/osx/lib
+    }
+
+    win* {
+        DEFINES += HAVE_PORTAUDIO
+    }
+    macx* {
+        DEFINES += HAVE_COREAUDIO HAVE_PORTAUDIO
     }
 }
 
-CONFIG += staticlib qt thread warn_on stl rtti exceptions
+CONFIG += staticlib qt thread warn_on stl rtti exceptions c++11
 QT += network xml gui widgets
 
 TARGET = svapp
 
-DEPENDPATH += . ../svcore ../svgui
-INCLUDEPATH += . ../svcore ../svgui
+DEPENDPATH += . ../bqaudioio ../svcore ../svgui
+INCLUDEPATH += . ../bqaudioio ../svcore ../svgui
 OBJECTS_DIR = o
 MOC_DIR = o
 
-win32-g++ {
-    INCLUDEPATH += ../sv-dependency-builds/win32-mingw/include
-}
-win32-msvc* {
-    INCLUDEPATH += ../sv-dependency-builds/win32-msvc/include
-}
+HEADERS += audio/AudioCallbackPlaySource.h \
+           audio/AudioRecordTarget.h \
+           audio/AudioGenerator.h \
+           audio/ClipMixer.h \
+           audio/ContinuousSynth.h \
+           audio/PlaySpeedRangeMapper.h
 
-HEADERS += audioio/AudioCallbackPlaySource.h \
-           audioio/AudioCallbackPlayTarget.h \
-           audioio/AudioCoreAudioTarget.h \
-           audioio/AudioGenerator.h \
-           audioio/AudioJACKTarget.h \
-           audioio/AudioPortAudioTarget.h \
-           audioio/AudioPulseAudioTarget.h \
-           audioio/AudioTargetFactory.h \
-           audioio/PlaySpeedRangeMapper.h
-SOURCES += audioio/AudioCallbackPlaySource.cpp \
-           audioio/AudioCallbackPlayTarget.cpp \
-           audioio/AudioCoreAudioTarget.cpp \
-           audioio/AudioGenerator.cpp \
-           audioio/AudioJACKTarget.cpp \
-           audioio/AudioPortAudioTarget.cpp \
-           audioio/AudioPulseAudioTarget.cpp \
-           audioio/AudioTargetFactory.cpp \
-           audioio/PlaySpeedRangeMapper.cpp
+SOURCES += audio/AudioCallbackPlaySource.cpp \
+           audio/AudioRecordTarget.cpp \
+           audio/AudioGenerator.cpp \
+           audio/ClipMixer.cpp \
+           audio/ContinuousSynth.cpp \
+           audio/PlaySpeedRangeMapper.cpp
 
 HEADERS += framework/Document.h \
            framework/MainWindowBase.h \