changeset 1238:dd49630e0d70 piper

Merge from branch project-file-rework
author Chris Cannam
date Fri, 28 Oct 2016 15:19:12 +0100
parents e699bdeef63c (current diff) a83541a1f100 (diff)
children 5261a7791f1c c401e738793f
files base/RealTime.cpp base/Resampler.cpp
diffstat 11 files changed, 975 insertions(+), 971 deletions(-) [+]
line wrap: on
line diff
--- a/base/RealTime.cpp	Tue Oct 25 10:53:12 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,480 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
-
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-/*
-   This is a modified version of a source file from the 
-   Rosegarden MIDI and audio sequencer and notation editor.
-   This file copyright 2000-2006 Chris Cannam.
-*/
-
-#include <iostream>
-
-#include <cstdlib>
-#include <sstream>
-
-#include "RealTime.h"
-
-#include "Debug.h"
-
-#include "Preferences.h"
-
-// A RealTime consists of two ints that must be at least 32 bits each.
-// A signed 32-bit int can store values exceeding +/- 2 billion.  This
-// means we can safely use our lower int for nanoseconds, as there are
-// 1 billion nanoseconds in a second and we need to handle double that
-// because of the implementations of addition etc that we use.
-//
-// The maximum valid RealTime on a 32-bit system is somewhere around
-// 68 years: 999999999 nanoseconds longer than the classic Unix epoch.
-
-#define ONE_BILLION 1000000000
-
-RealTime::RealTime(int s, int n) :
-    sec(s), nsec(n)
-{
-    if (sec == 0) {
-	while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; }
-	while (nsec >=  ONE_BILLION) { nsec -= ONE_BILLION; ++sec; }
-    } else if (sec < 0) {
-	while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; }
-	while (nsec > 0)             { nsec -= ONE_BILLION; ++sec; }
-    } else { 
-	while (nsec >=  ONE_BILLION) { nsec -= ONE_BILLION; ++sec; }
-	while (nsec < 0)             { nsec += ONE_BILLION; --sec; }
-    }
-}
-
-RealTime
-RealTime::fromSeconds(double sec)
-{
-    if (sec >= 0) {
-        return RealTime(int(sec), int((sec - int(sec)) * ONE_BILLION + 0.5));
-    } else {
-        return -fromSeconds(-sec);
-    }
-}
-
-RealTime
-RealTime::fromMilliseconds(int msec)
-{
-    return RealTime(msec / 1000, (msec % 1000) * 1000000);
-}
-
-RealTime
-RealTime::fromTimeval(const struct timeval &tv)
-{
-    return RealTime(int(tv.tv_sec), int(tv.tv_usec * 1000));
-}
-
-RealTime
-RealTime::fromXsdDuration(std::string xsdd)
-{
-    RealTime t;
-
-    int year = 0, month = 0, day = 0, hour = 0, minute = 0;
-    double second = 0.0;
-
-    int i = 0;
-
-    const char *s = xsdd.c_str();
-    int len = int(xsdd.length());
-
-    bool negative = false, afterT = false;
-
-    while (i < len) {
-
-        if (s[i] == '-') {
-            if (i == 0) negative = true;
-            ++i;
-            continue;
-        }
-
-        double value = 0.0;
-        char *eptr = 0;
-
-        if (isdigit(s[i]) || s[i] == '.') {
-            value = strtod(&s[i], &eptr);
-            i = int(eptr - s);
-        }
-
-        if (i == len) break;
-
-        switch (s[i]) {
-        case 'Y': year = int(value + 0.1); break;
-        case 'D': day  = int(value + 0.1); break;
-        case 'H': hour = int(value + 0.1); break;
-        case 'M':
-            if (afterT) minute = int(value + 0.1);
-            else month = int(value + 0.1);
-            break;
-        case 'S':
-            second = value;
-            break;
-        case 'T': afterT = true; break;
-        };
-
-        ++i;
-    }
-
-    if (year > 0) {
-        cerr << "WARNING: This xsd:duration (\"" << xsdd << "\") contains a non-zero year.\nWith no origin and a limited data size, I will treat a year as exactly 31556952\nseconds and you should expect overflow and/or poor results." << endl;
-        t = t + RealTime(year * 31556952, 0);
-    }
-
-    if (month > 0) {
-        cerr << "WARNING: This xsd:duration (\"" << xsdd << "\") contains a non-zero month.\nWith no origin and a limited data size, I will treat a month as exactly 2629746\nseconds and you should expect overflow and/or poor results." << endl;
-        t = t + RealTime(month * 2629746, 0);
-    }
-
-    if (day > 0) {
-        t = t + RealTime(day * 86400, 0);
-    }
-
-    if (hour > 0) {
-        t = t + RealTime(hour * 3600, 0);
-    }
-
-    if (minute > 0) {
-        t = t + RealTime(minute * 60, 0);
-    }
-
-    t = t + fromSeconds(second);
-
-    if (negative) {
-        return -t;
-    } else {
-        return t;
-    }
-}
-
-double
-RealTime::toDouble() const
-{
-    double d = sec;
-    d += double(nsec) / double(ONE_BILLION);
-    return d;
-}
-
-std::ostream &operator<<(std::ostream &out, const RealTime &rt)
-{
-    if (rt < RealTime::zeroTime) {
-	out << "-";
-    } else {
-	out << " ";
-    }
-
-    int s = (rt.sec < 0 ? -rt.sec : rt.sec);
-    int n = (rt.nsec < 0 ? -rt.nsec : rt.nsec);
-
-    out << s << ".";
-
-    int nn(n);
-    if (nn == 0) out << "00000000";
-    else while (nn < (ONE_BILLION / 10)) {
-	out << "0";
-	nn *= 10;
-    }
-    
-    out << n << "R";
-    return out;
-}
-
-std::string
-RealTime::toString(bool align) const
-{
-    std::stringstream out;
-    out << *this;
-    
-    std::string s = out.str();
-
-    if (!align && *this >= RealTime::zeroTime) {
-        // remove leading " "
-        s = s.substr(1, s.length() - 1);
-    }
-
-    // remove trailing R
-    return s.substr(0, s.length() - 1);
-}
-
-RealTime
-RealTime::fromString(std::string s)
-{
-    bool negative = false;
-    int section = 0;
-    std::string ssec, snsec;
-
-    for (size_t i = 0; i < s.length(); ++i) {
-
-        char c = s[i];
-        if (isspace(c)) continue;
-
-        if (section == 0) {
-
-            if (c == '-') negative = true;
-            else if (isdigit(c)) { section = 1; ssec += c; }
-            else if (c == '.') section = 2;
-            else break;
-
-        } else if (section == 1) {
-
-            if (c == '.') section = 2;
-            else if (isdigit(c)) ssec += c;
-            else break;
-
-        } else if (section == 2) {
-
-            if (isdigit(c)) snsec += c;
-            else break;
-        }
-    }
-
-    while (snsec.length() < 8) snsec += '0';
-
-    int sec = atoi(ssec.c_str());
-    int nsec = atoi(snsec.c_str());
-    if (negative) sec = -sec;
-
-//    SVDEBUG << "RealTime::fromString: string " << s << " -> "
-//              << sec << " sec, " << nsec << " nsec" << endl;
-
-    return RealTime(sec, nsec);
-}
-
-std::string
-RealTime::toText(bool fixedDp) const
-{
-    if (*this < RealTime::zeroTime) return "-" + (-*this).toText(fixedDp);
-
-    Preferences *p = Preferences::getInstance();
-    bool hms = true;
-    
-    if (p) {
-        hms = p->getShowHMS();
-        int fps = 0;
-        switch (p->getTimeToTextMode()) {
-        case Preferences::TimeToTextMs: break;
-        case Preferences::TimeToTextUs: fps = 1000000; break;
-        case Preferences::TimeToText24Frame: fps = 24; break;
-        case Preferences::TimeToText25Frame: fps = 25; break;
-        case Preferences::TimeToText30Frame: fps = 30; break;
-        case Preferences::TimeToText50Frame: fps = 50; break;
-        case Preferences::TimeToText60Frame: fps = 60; break;
-        }
-        if (fps != 0) return toFrameText(fps, hms);
-    }
-
-    return toMSText(fixedDp, hms);
-}
-
-static void
-writeSecPart(std::stringstream &out, bool hms, int sec)
-{
-    if (hms) {
-        if (sec >= 3600) {
-            out << (sec / 3600) << ":";
-        }
-
-        if (sec >= 60) {
-            int minutes = (sec % 3600) / 60;
-            if (sec >= 3600 && minutes < 10) out << "0";
-            out << minutes << ":";
-        }
-
-        if (sec >= 10) {
-            out << ((sec % 60) / 10);
-        }
-
-        out << (sec % 10);
-
-    } else {
-        out << sec;
-    }
-}
-
-std::string
-RealTime::toMSText(bool fixedDp, bool hms) const
-{
-    if (*this < RealTime::zeroTime) return "-" + (-*this).toMSText(fixedDp, hms);
-
-    std::stringstream out;
-
-    writeSecPart(out, hms, sec);
-    
-    int ms = msec();
-
-    if (ms != 0) {
-	out << ".";
-	out << (ms / 100);
-	ms = ms % 100;
-	if (ms != 0) {
-	    out << (ms / 10);
-	    ms = ms % 10;
-	} else if (fixedDp) {
-	    out << "0";
-	}
-	if (ms != 0) {
-	    out << ms;
-	} else if (fixedDp) {
-	    out << "0";
-	}
-    } else if (fixedDp) {
-	out << ".000";
-    }
-	
-    std::string s = out.str();
-
-    return s;
-}
-
-std::string
-RealTime::toFrameText(int fps, bool hms) const
-{
-    if (*this < RealTime::zeroTime) return "-" + (-*this).toFrameText(fps, hms);
-
-    std::stringstream out;
-
-    writeSecPart(out, hms, sec);
-
-    // avoid rounding error if fps does not divide into ONE_BILLION
-    int64_t fbig = nsec;
-    fbig *= fps;
-    int f = int(fbig / ONE_BILLION);
-
-    int div = 1;
-    int n = fps - 1;
-    while ((n = n / 10)) {
-        div *= 10;
-    }
-
-    out << ":";
-
-//    cerr << "div = " << div << ", f =  "<< f << endl;
-
-    while (div) {
-        int d = (f / div) % 10;
-        out << d;
-        div /= 10;
-    }
-	
-    std::string s = out.str();
-
-//    cerr << "converted " << toString() << " to " << s << endl;
-
-    return s;
-}
-
-std::string
-RealTime::toSecText() const
-{
-    if (*this < RealTime::zeroTime) return "-" + (-*this).toSecText();
-
-    std::stringstream out;
-
-    writeSecPart(out, true, sec);
-    
-    if (sec < 60) {
-        out << "s";
-    }
-
-    std::string s = out.str();
-
-    return s;
-}
-
-std::string
-RealTime::toXsdDuration() const
-{
-    std::string s = "PT" + toString(false) + "S";
-    return s;
-}
-
-RealTime
-RealTime::operator*(int m) const
-{
-    double t = (double(nsec) / ONE_BILLION) * m;
-    t += sec * m;
-    return fromSeconds(t);
-}
-
-RealTime
-RealTime::operator/(int d) const
-{
-    int secdiv = sec / d;
-    int secrem = sec % d;
-
-    double nsecdiv = (double(nsec) + ONE_BILLION * double(secrem)) / d;
-    
-    return RealTime(secdiv, int(nsecdiv + 0.5));
-}
-
-RealTime
-RealTime::operator*(double m) const
-{
-    double t = (double(nsec) / ONE_BILLION) * m;
-    t += sec * m;
-    return fromSeconds(t);
-}
-
-RealTime
-RealTime::operator/(double d) const
-{
-    double t = (double(nsec) / ONE_BILLION) / d;
-    t += sec / d;
-    return fromSeconds(t);
-}
-
-double 
-RealTime::operator/(const RealTime &r) const
-{
-    double lTotal = double(sec) * ONE_BILLION + double(nsec);
-    double rTotal = double(r.sec) * ONE_BILLION + double(r.nsec);
-    
-    if (rTotal == 0) return 0.0;
-    else return lTotal/rTotal;
-}
-
-static RealTime
-frame2RealTime_i(sv_frame_t frame, sv_frame_t iSampleRate)
-{
-    if (frame < 0) return -frame2RealTime_i(-frame, iSampleRate);
-
-    RealTime rt;
-    sv_frame_t sec = frame / iSampleRate;
-    rt.sec = int(sec);
-    frame -= sec * iSampleRate;
-    rt.nsec = (int)(((double(frame) * 1000000.0) / double(iSampleRate)) * 1000.0);
-    return rt;
-}
-
-sv_frame_t
-RealTime::realTime2Frame(const RealTime &time, sv_samplerate_t sampleRate)
-{
-    if (time < zeroTime) return -realTime2Frame(-time, sampleRate);
-    double s = time.sec + double(time.nsec + 1) / 1000000000.0;
-    return sv_frame_t(s * sampleRate);
-}
-
-RealTime
-RealTime::frame2RealTime(sv_frame_t frame, sv_samplerate_t sampleRate)
-{
-    if (sampleRate == double(int(sampleRate))) {
-        return frame2RealTime_i(frame, int(sampleRate));
-    }
-
-    double sec = double(frame) / sampleRate;
-    return fromSeconds(sec);
-}
-
-const RealTime RealTime::zeroTime(0,0);
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/RealTimeSV.cpp	Fri Oct 28 15:19:12 2016 +0100
@@ -0,0 +1,480 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rosegarden MIDI and audio sequencer and notation editor.
+   This file copyright 2000-2006 Chris Cannam.
+*/
+
+#include <iostream>
+
+#include <cstdlib>
+#include <sstream>
+
+#include "RealTime.h"
+
+#include "Debug.h"
+
+#include "Preferences.h"
+
+// A RealTime consists of two ints that must be at least 32 bits each.
+// A signed 32-bit int can store values exceeding +/- 2 billion.  This
+// means we can safely use our lower int for nanoseconds, as there are
+// 1 billion nanoseconds in a second and we need to handle double that
+// because of the implementations of addition etc that we use.
+//
+// The maximum valid RealTime on a 32-bit system is somewhere around
+// 68 years: 999999999 nanoseconds longer than the classic Unix epoch.
+
+#define ONE_BILLION 1000000000
+
+RealTime::RealTime(int s, int n) :
+    sec(s), nsec(n)
+{
+    if (sec == 0) {
+	while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; }
+	while (nsec >=  ONE_BILLION) { nsec -= ONE_BILLION; ++sec; }
+    } else if (sec < 0) {
+	while (nsec <= -ONE_BILLION) { nsec += ONE_BILLION; --sec; }
+	while (nsec > 0)             { nsec -= ONE_BILLION; ++sec; }
+    } else { 
+	while (nsec >=  ONE_BILLION) { nsec -= ONE_BILLION; ++sec; }
+	while (nsec < 0)             { nsec += ONE_BILLION; --sec; }
+    }
+}
+
+RealTime
+RealTime::fromSeconds(double sec)
+{
+    if (sec >= 0) {
+        return RealTime(int(sec), int((sec - int(sec)) * ONE_BILLION + 0.5));
+    } else {
+        return -fromSeconds(-sec);
+    }
+}
+
+RealTime
+RealTime::fromMilliseconds(int msec)
+{
+    return RealTime(msec / 1000, (msec % 1000) * 1000000);
+}
+
+RealTime
+RealTime::fromTimeval(const struct timeval &tv)
+{
+    return RealTime(int(tv.tv_sec), int(tv.tv_usec * 1000));
+}
+
+RealTime
+RealTime::fromXsdDuration(std::string xsdd)
+{
+    RealTime t;
+
+    int year = 0, month = 0, day = 0, hour = 0, minute = 0;
+    double second = 0.0;
+
+    int i = 0;
+
+    const char *s = xsdd.c_str();
+    int len = int(xsdd.length());
+
+    bool negative = false, afterT = false;
+
+    while (i < len) {
+
+        if (s[i] == '-') {
+            if (i == 0) negative = true;
+            ++i;
+            continue;
+        }
+
+        double value = 0.0;
+        char *eptr = 0;
+
+        if (isdigit(s[i]) || s[i] == '.') {
+            value = strtod(&s[i], &eptr);
+            i = int(eptr - s);
+        }
+
+        if (i == len) break;
+
+        switch (s[i]) {
+        case 'Y': year = int(value + 0.1); break;
+        case 'D': day  = int(value + 0.1); break;
+        case 'H': hour = int(value + 0.1); break;
+        case 'M':
+            if (afterT) minute = int(value + 0.1);
+            else month = int(value + 0.1);
+            break;
+        case 'S':
+            second = value;
+            break;
+        case 'T': afterT = true; break;
+        };
+
+        ++i;
+    }
+
+    if (year > 0) {
+        cerr << "WARNING: This xsd:duration (\"" << xsdd << "\") contains a non-zero year.\nWith no origin and a limited data size, I will treat a year as exactly 31556952\nseconds and you should expect overflow and/or poor results." << endl;
+        t = t + RealTime(year * 31556952, 0);
+    }
+
+    if (month > 0) {
+        cerr << "WARNING: This xsd:duration (\"" << xsdd << "\") contains a non-zero month.\nWith no origin and a limited data size, I will treat a month as exactly 2629746\nseconds and you should expect overflow and/or poor results." << endl;
+        t = t + RealTime(month * 2629746, 0);
+    }
+
+    if (day > 0) {
+        t = t + RealTime(day * 86400, 0);
+    }
+
+    if (hour > 0) {
+        t = t + RealTime(hour * 3600, 0);
+    }
+
+    if (minute > 0) {
+        t = t + RealTime(minute * 60, 0);
+    }
+
+    t = t + fromSeconds(second);
+
+    if (negative) {
+        return -t;
+    } else {
+        return t;
+    }
+}
+
+double
+RealTime::toDouble() const
+{
+    double d = sec;
+    d += double(nsec) / double(ONE_BILLION);
+    return d;
+}
+
+std::ostream &operator<<(std::ostream &out, const RealTime &rt)
+{
+    if (rt < RealTime::zeroTime) {
+	out << "-";
+    } else {
+	out << " ";
+    }
+
+    int s = (rt.sec < 0 ? -rt.sec : rt.sec);
+    int n = (rt.nsec < 0 ? -rt.nsec : rt.nsec);
+
+    out << s << ".";
+
+    int nn(n);
+    if (nn == 0) out << "00000000";
+    else while (nn < (ONE_BILLION / 10)) {
+	out << "0";
+	nn *= 10;
+    }
+    
+    out << n << "R";
+    return out;
+}
+
+std::string
+RealTime::toString(bool align) const
+{
+    std::stringstream out;
+    out << *this;
+    
+    std::string s = out.str();
+
+    if (!align && *this >= RealTime::zeroTime) {
+        // remove leading " "
+        s = s.substr(1, s.length() - 1);
+    }
+
+    // remove trailing R
+    return s.substr(0, s.length() - 1);
+}
+
+RealTime
+RealTime::fromString(std::string s)
+{
+    bool negative = false;
+    int section = 0;
+    std::string ssec, snsec;
+
+    for (size_t i = 0; i < s.length(); ++i) {
+
+        char c = s[i];
+        if (isspace(c)) continue;
+
+        if (section == 0) {
+
+            if (c == '-') negative = true;
+            else if (isdigit(c)) { section = 1; ssec += c; }
+            else if (c == '.') section = 2;
+            else break;
+
+        } else if (section == 1) {
+
+            if (c == '.') section = 2;
+            else if (isdigit(c)) ssec += c;
+            else break;
+
+        } else if (section == 2) {
+
+            if (isdigit(c)) snsec += c;
+            else break;
+        }
+    }
+
+    while (snsec.length() < 8) snsec += '0';
+
+    int sec = atoi(ssec.c_str());
+    int nsec = atoi(snsec.c_str());
+    if (negative) sec = -sec;
+
+//    SVDEBUG << "RealTime::fromString: string " << s << " -> "
+//              << sec << " sec, " << nsec << " nsec" << endl;
+
+    return RealTime(sec, nsec);
+}
+
+std::string
+RealTime::toText(bool fixedDp) const
+{
+    if (*this < RealTime::zeroTime) return "-" + (-*this).toText(fixedDp);
+
+    Preferences *p = Preferences::getInstance();
+    bool hms = true;
+    
+    if (p) {
+        hms = p->getShowHMS();
+        int fps = 0;
+        switch (p->getTimeToTextMode()) {
+        case Preferences::TimeToTextMs: break;
+        case Preferences::TimeToTextUs: fps = 1000000; break;
+        case Preferences::TimeToText24Frame: fps = 24; break;
+        case Preferences::TimeToText25Frame: fps = 25; break;
+        case Preferences::TimeToText30Frame: fps = 30; break;
+        case Preferences::TimeToText50Frame: fps = 50; break;
+        case Preferences::TimeToText60Frame: fps = 60; break;
+        }
+        if (fps != 0) return toFrameText(fps, hms);
+    }
+
+    return toMSText(fixedDp, hms);
+}
+
+static void
+writeSecPart(std::stringstream &out, bool hms, int sec)
+{
+    if (hms) {
+        if (sec >= 3600) {
+            out << (sec / 3600) << ":";
+        }
+
+        if (sec >= 60) {
+            int minutes = (sec % 3600) / 60;
+            if (sec >= 3600 && minutes < 10) out << "0";
+            out << minutes << ":";
+        }
+
+        if (sec >= 10) {
+            out << ((sec % 60) / 10);
+        }
+
+        out << (sec % 10);
+
+    } else {
+        out << sec;
+    }
+}
+
+std::string
+RealTime::toMSText(bool fixedDp, bool hms) const
+{
+    if (*this < RealTime::zeroTime) return "-" + (-*this).toMSText(fixedDp, hms);
+
+    std::stringstream out;
+
+    writeSecPart(out, hms, sec);
+    
+    int ms = msec();
+
+    if (ms != 0) {
+	out << ".";
+	out << (ms / 100);
+	ms = ms % 100;
+	if (ms != 0) {
+	    out << (ms / 10);
+	    ms = ms % 10;
+	} else if (fixedDp) {
+	    out << "0";
+	}
+	if (ms != 0) {
+	    out << ms;
+	} else if (fixedDp) {
+	    out << "0";
+	}
+    } else if (fixedDp) {
+	out << ".000";
+    }
+	
+    std::string s = out.str();
+
+    return s;
+}
+
+std::string
+RealTime::toFrameText(int fps, bool hms) const
+{
+    if (*this < RealTime::zeroTime) return "-" + (-*this).toFrameText(fps, hms);
+
+    std::stringstream out;
+
+    writeSecPart(out, hms, sec);
+
+    // avoid rounding error if fps does not divide into ONE_BILLION
+    int64_t fbig = nsec;
+    fbig *= fps;
+    int f = int(fbig / ONE_BILLION);
+
+    int div = 1;
+    int n = fps - 1;
+    while ((n = n / 10)) {
+        div *= 10;
+    }
+
+    out << ":";
+
+//    cerr << "div = " << div << ", f =  "<< f << endl;
+
+    while (div) {
+        int d = (f / div) % 10;
+        out << d;
+        div /= 10;
+    }
+	
+    std::string s = out.str();
+
+//    cerr << "converted " << toString() << " to " << s << endl;
+
+    return s;
+}
+
+std::string
+RealTime::toSecText() const
+{
+    if (*this < RealTime::zeroTime) return "-" + (-*this).toSecText();
+
+    std::stringstream out;
+
+    writeSecPart(out, true, sec);
+    
+    if (sec < 60) {
+        out << "s";
+    }
+
+    std::string s = out.str();
+
+    return s;
+}
+
+std::string
+RealTime::toXsdDuration() const
+{
+    std::string s = "PT" + toString(false) + "S";
+    return s;
+}
+
+RealTime
+RealTime::operator*(int m) const
+{
+    double t = (double(nsec) / ONE_BILLION) * m;
+    t += sec * m;
+    return fromSeconds(t);
+}
+
+RealTime
+RealTime::operator/(int d) const
+{
+    int secdiv = sec / d;
+    int secrem = sec % d;
+
+    double nsecdiv = (double(nsec) + ONE_BILLION * double(secrem)) / d;
+    
+    return RealTime(secdiv, int(nsecdiv + 0.5));
+}
+
+RealTime
+RealTime::operator*(double m) const
+{
+    double t = (double(nsec) / ONE_BILLION) * m;
+    t += sec * m;
+    return fromSeconds(t);
+}
+
+RealTime
+RealTime::operator/(double d) const
+{
+    double t = (double(nsec) / ONE_BILLION) / d;
+    t += sec / d;
+    return fromSeconds(t);
+}
+
+double 
+RealTime::operator/(const RealTime &r) const
+{
+    double lTotal = double(sec) * ONE_BILLION + double(nsec);
+    double rTotal = double(r.sec) * ONE_BILLION + double(r.nsec);
+    
+    if (rTotal == 0) return 0.0;
+    else return lTotal/rTotal;
+}
+
+static RealTime
+frame2RealTime_i(sv_frame_t frame, sv_frame_t iSampleRate)
+{
+    if (frame < 0) return -frame2RealTime_i(-frame, iSampleRate);
+
+    RealTime rt;
+    sv_frame_t sec = frame / iSampleRate;
+    rt.sec = int(sec);
+    frame -= sec * iSampleRate;
+    rt.nsec = (int)(((double(frame) * 1000000.0) / double(iSampleRate)) * 1000.0);
+    return rt;
+}
+
+sv_frame_t
+RealTime::realTime2Frame(const RealTime &time, sv_samplerate_t sampleRate)
+{
+    if (time < zeroTime) return -realTime2Frame(-time, sampleRate);
+    double s = time.sec + double(time.nsec + 1) / 1000000000.0;
+    return sv_frame_t(s * sampleRate);
+}
+
+RealTime
+RealTime::frame2RealTime(sv_frame_t frame, sv_samplerate_t sampleRate)
+{
+    if (sampleRate == double(int(sampleRate))) {
+        return frame2RealTime_i(frame, int(sampleRate));
+    }
+
+    double sec = double(frame) / sampleRate;
+    return fromSeconds(sec);
+}
+
+const RealTime RealTime::zeroTime(0,0);
+
--- a/base/Resampler.cpp	Tue Oct 25 10:53:12 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,196 +0,0 @@
-/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
-/*
-    Sonic Visualiser
-    An audio file viewer and annotation editor.
-    Centre for Digital Music, Queen Mary, University of London.
-    
-    This program is free software; you can redistribute it and/or
-    modify it under the terms of the GNU General Public License as
-    published by the Free Software Foundation; either version 2 of the
-    License, or (at your option) any later version.  See the file
-    COPYING included with this distribution for more information.
-*/
-
-/*
-   This is a modified version of a source file from the 
-   Rubber Band audio timestretcher library.
-   This file copyright 2007 Chris Cannam.
-*/
-
-#include "Resampler.h"
-
-#include <cstdlib>
-#include <cmath>
-
-#include <iostream>
-
-#include <samplerate.h>
-
-#include "Debug.h"
-
-class Resampler::D
-{
-public:
-    D(Quality quality, int channels, sv_frame_t chunkSize);
-    ~D();
-
-    sv_frame_t resample(float **in, float **out,
-                 sv_frame_t incount, double ratio,
-                 bool final);
-
-    sv_frame_t resampleInterleaved(float *in, float *out,
-                            sv_frame_t incount, double ratio,
-                            bool final);
-
-    void reset();
-
-protected:
-    SRC_STATE *m_src;
-    float *m_iin;
-    float *m_iout;
-    int m_channels;
-    sv_frame_t m_iinsize;
-    sv_frame_t m_ioutsize;
-};
-
-Resampler::D::D(Quality quality, int channels, sv_frame_t chunkSize) :
-    m_src(0),
-    m_iin(0),
-    m_iout(0),
-    m_channels(channels),
-    m_iinsize(0),
-    m_ioutsize(0)
-{
-    int err = 0;
-    m_src = src_new(quality == Best ? SRC_SINC_BEST_QUALITY :
-                    quality == Fastest ? SRC_LINEAR :
-                    SRC_SINC_FASTEST,
-                    channels, &err);
-
-    //!!! check err, throw
-
-    if (chunkSize > 0 && m_channels > 1) {
-        //!!! alignment?
-        m_iinsize = chunkSize * m_channels;
-        m_ioutsize = chunkSize * m_channels * 2;
-        m_iin = (float *)malloc(m_iinsize * sizeof(float));
-        m_iout = (float *)malloc(m_ioutsize * sizeof(float));
-    }
-}
-
-Resampler::D::~D()
-{
-    src_delete(m_src);
-    if (m_iinsize > 0) {
-        free(m_iin);
-    }
-    if (m_ioutsize > 0) {
-        free(m_iout);
-    }
-}
-
-sv_frame_t
-Resampler::D::resample(float **in, float **out,
-                       sv_frame_t incount, double ratio,
-                       bool final)
-{
-    if (m_channels == 1) {
-        return resampleInterleaved(*in, *out, incount, ratio, final);
-    }
-
-    sv_frame_t outcount = lrint(ceil(double(incount) * ratio));
-
-    if (incount * m_channels > m_iinsize) {
-        m_iinsize = incount * m_channels;
-        m_iin = (float *)realloc(m_iin, m_iinsize * sizeof(float));
-    }
-    if (outcount * m_channels > m_ioutsize) {
-        m_ioutsize = outcount * m_channels;
-        m_iout = (float *)realloc(m_iout, m_ioutsize * sizeof(float));
-    }
-    for (sv_frame_t i = 0; i < incount; ++i) {
-        for (int c = 0; c < m_channels; ++c) {
-            m_iin[i * m_channels + c] = in[c][i];
-        }
-    }
-    
-    sv_frame_t gen = resampleInterleaved(m_iin, m_iout, incount, ratio, final);
-
-    for (sv_frame_t i = 0; i < gen; ++i) {
-        for (int c = 0; c < m_channels; ++c) {
-            out[c][i] = m_iout[i * m_channels + c];
-        }
-    }
-
-    return gen;
-}
-
-sv_frame_t
-Resampler::D::resampleInterleaved(float *in, float *out,
-                                  sv_frame_t incount, double ratio,
-                                  bool final)
-{
-    SRC_DATA data;
-
-    sv_frame_t outcount = lrint(ceil(double(incount) * ratio));
-
-    data.data_in = in;
-    data.data_out = out;
-    data.input_frames = incount;
-    data.output_frames = outcount;
-    data.src_ratio = ratio;
-    data.end_of_input = (final ? 1 : 0);
-
-    int err = src_process(m_src, &data);
-
-    if (err) {
-        cerr << "Resampler: ERROR: src_process returned error: " <<
-            src_strerror(err) << endl;
-        return 0;
-    }
-
-    if (data.input_frames_used != incount) {
-        cerr << "Resampler: NOTE: input_frames_used == " << data.input_frames_used << " (while incount = " << incount << ")" << endl;
-    }
-
-    return data.output_frames_gen;
-}
-
-void
-Resampler::D::reset()
-{
-    src_reset(m_src);
-}
-
-Resampler::Resampler(Quality quality, int channels, sv_frame_t chunkSize)
-{
-    m_d = new D(quality, channels, chunkSize);
-}
-
-Resampler::~Resampler()
-{
-    delete m_d;
-}
-
-sv_frame_t 
-Resampler::resample(float **in, float **out,
-                    sv_frame_t incount, double ratio,
-                    bool final)
-{
-    return m_d->resample(in, out, incount, ratio, final);
-}
-
-sv_frame_t 
-Resampler::resampleInterleaved(float *in, float *out,
-                               sv_frame_t incount, double ratio,
-                               bool final)
-{
-    return m_d->resampleInterleaved(in, out, incount, ratio, final);
-}
-
-void
-Resampler::reset()
-{
-    m_d->reset();
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/base/ResamplerSV.cpp	Fri Oct 28 15:19:12 2016 +0100
@@ -0,0 +1,196 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+/*
+    Sonic Visualiser
+    An audio file viewer and annotation editor.
+    Centre for Digital Music, Queen Mary, University of London.
+    
+    This program is free software; you can redistribute it and/or
+    modify it under the terms of the GNU General Public License as
+    published by the Free Software Foundation; either version 2 of the
+    License, or (at your option) any later version.  See the file
+    COPYING included with this distribution for more information.
+*/
+
+/*
+   This is a modified version of a source file from the 
+   Rubber Band audio timestretcher library.
+   This file copyright 2007 Chris Cannam.
+*/
+
+#include "Resampler.h"
+
+#include <cstdlib>
+#include <cmath>
+
+#include <iostream>
+
+#include <samplerate.h>
+
+#include "Debug.h"
+
+class Resampler::D
+{
+public:
+    D(Quality quality, int channels, sv_frame_t chunkSize);
+    ~D();
+
+    sv_frame_t resample(float **in, float **out,
+                 sv_frame_t incount, double ratio,
+                 bool final);
+
+    sv_frame_t resampleInterleaved(float *in, float *out,
+                            sv_frame_t incount, double ratio,
+                            bool final);
+
+    void reset();
+
+protected:
+    SRC_STATE *m_src;
+    float *m_iin;
+    float *m_iout;
+    int m_channels;
+    sv_frame_t m_iinsize;
+    sv_frame_t m_ioutsize;
+};
+
+Resampler::D::D(Quality quality, int channels, sv_frame_t chunkSize) :
+    m_src(0),
+    m_iin(0),
+    m_iout(0),
+    m_channels(channels),
+    m_iinsize(0),
+    m_ioutsize(0)
+{
+    int err = 0;
+    m_src = src_new(quality == Best ? SRC_SINC_BEST_QUALITY :
+                    quality == Fastest ? SRC_LINEAR :
+                    SRC_SINC_FASTEST,
+                    channels, &err);
+
+    //!!! check err, throw
+
+    if (chunkSize > 0 && m_channels > 1) {
+        //!!! alignment?
+        m_iinsize = chunkSize * m_channels;
+        m_ioutsize = chunkSize * m_channels * 2;
+        m_iin = (float *)malloc(m_iinsize * sizeof(float));
+        m_iout = (float *)malloc(m_ioutsize * sizeof(float));
+    }
+}
+
+Resampler::D::~D()
+{
+    src_delete(m_src);
+    if (m_iinsize > 0) {
+        free(m_iin);
+    }
+    if (m_ioutsize > 0) {
+        free(m_iout);
+    }
+}
+
+sv_frame_t
+Resampler::D::resample(float **in, float **out,
+                       sv_frame_t incount, double ratio,
+                       bool final)
+{
+    if (m_channels == 1) {
+        return resampleInterleaved(*in, *out, incount, ratio, final);
+    }
+
+    sv_frame_t outcount = lrint(ceil(double(incount) * ratio));
+
+    if (incount * m_channels > m_iinsize) {
+        m_iinsize = incount * m_channels;
+        m_iin = (float *)realloc(m_iin, m_iinsize * sizeof(float));
+    }
+    if (outcount * m_channels > m_ioutsize) {
+        m_ioutsize = outcount * m_channels;
+        m_iout = (float *)realloc(m_iout, m_ioutsize * sizeof(float));
+    }
+    for (sv_frame_t i = 0; i < incount; ++i) {
+        for (int c = 0; c < m_channels; ++c) {
+            m_iin[i * m_channels + c] = in[c][i];
+        }
+    }
+    
+    sv_frame_t gen = resampleInterleaved(m_iin, m_iout, incount, ratio, final);
+
+    for (sv_frame_t i = 0; i < gen; ++i) {
+        for (int c = 0; c < m_channels; ++c) {
+            out[c][i] = m_iout[i * m_channels + c];
+        }
+    }
+
+    return gen;
+}
+
+sv_frame_t
+Resampler::D::resampleInterleaved(float *in, float *out,
+                                  sv_frame_t incount, double ratio,
+                                  bool final)
+{
+    SRC_DATA data;
+
+    sv_frame_t outcount = lrint(ceil(double(incount) * ratio));
+
+    data.data_in = in;
+    data.data_out = out;
+    data.input_frames = incount;
+    data.output_frames = outcount;
+    data.src_ratio = ratio;
+    data.end_of_input = (final ? 1 : 0);
+
+    int err = src_process(m_src, &data);
+
+    if (err) {
+        cerr << "Resampler: ERROR: src_process returned error: " <<
+            src_strerror(err) << endl;
+        return 0;
+    }
+
+    if (data.input_frames_used != incount) {
+        cerr << "Resampler: NOTE: input_frames_used == " << data.input_frames_used << " (while incount = " << incount << ")" << endl;
+    }
+
+    return data.output_frames_gen;
+}
+
+void
+Resampler::D::reset()
+{
+    src_reset(m_src);
+}
+
+Resampler::Resampler(Quality quality, int channels, sv_frame_t chunkSize)
+{
+    m_d = new D(quality, channels, chunkSize);
+}
+
+Resampler::~Resampler()
+{
+    delete m_d;
+}
+
+sv_frame_t 
+Resampler::resample(float **in, float **out,
+                    sv_frame_t incount, double ratio,
+                    bool final)
+{
+    return m_d->resample(in, out, incount, ratio, final);
+}
+
+sv_frame_t 
+Resampler::resampleInterleaved(float *in, float *out,
+                               sv_frame_t incount, double ratio,
+                               bool final)
+{
+    return m_d->resampleInterleaved(in, out, incount, ratio, final);
+}
+
+void
+Resampler::reset()
+{
+    m_d->reset();
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/files.pri	Fri Oct 28 15:19:12 2016 +0100
@@ -0,0 +1,247 @@
+SVCORE_HEADERS = \
+           base/AudioLevel.h \
+           base/AudioPlaySource.h \
+           base/BaseTypes.h \
+           base/Clipboard.h \
+           base/ColumnOp.h \
+           base/Command.h \
+           base/Debug.h \
+           base/Exceptions.h \
+           base/LogRange.h \
+           base/MagnitudeRange.h \
+           base/Pitch.h \
+           base/Playable.h \
+           base/PlayParameterRepository.h \
+           base/PlayParameters.h \
+           base/Preferences.h \
+           base/Profiler.h \
+           base/ProgressPrinter.h \
+           base/ProgressReporter.h \
+           base/PropertyContainer.h \
+           base/RangeMapper.h \
+           base/RealTime.h \
+           base/RecentFiles.h \
+           base/Resampler.h \
+           base/ResourceFinder.h \
+           base/RingBuffer.h \
+           base/Scavenger.h \
+           base/Selection.h \
+           base/Serialiser.h \
+           base/StorageAdviser.h \
+           base/StringBits.h \
+           base/Strings.h \
+           base/TempDirectory.h \
+           base/TempWriteFile.h \
+           base/TextMatcher.h \
+           base/Thread.h \
+           base/UnitDatabase.h \
+           base/ViewManagerBase.h \
+           base/Window.h \
+           base/XmlExportable.h \
+           base/ZoomConstraint.h \
+	   data/fft/FFTapi.h \
+           data/fileio/AudioFileReader.h \
+           data/fileio/AudioFileReaderFactory.h \
+           data/fileio/AudioFileSizeEstimator.h \
+           data/fileio/BZipFileDevice.h \
+           data/fileio/CachedFile.h \
+           data/fileio/CodedAudioFileReader.h \
+           data/fileio/CSVFileReader.h \
+           data/fileio/CSVFileWriter.h \
+           data/fileio/CSVFormat.h \
+           data/fileio/DataFileReader.h \
+           data/fileio/DataFileReaderFactory.h \
+           data/fileio/FileFinder.h \
+           data/fileio/FileReadThread.h \
+           data/fileio/FileSource.h \
+           data/fileio/MIDIFileReader.h \
+           data/fileio/MIDIFileWriter.h \
+           data/fileio/MP3FileReader.h \
+           data/fileio/OggVorbisFileReader.h \
+           data/fileio/PlaylistFileReader.h \
+           data/fileio/QuickTimeFileReader.h \
+           data/fileio/CoreAudioFileReader.h \
+           data/fileio/DecodingWavFileReader.h \
+           data/fileio/WavFileReader.h \
+           data/fileio/WavFileWriter.h \
+           data/midi/MIDIEvent.h \
+           data/midi/MIDIInput.h \
+           data/midi/rtmidi/RtError.h \
+           data/midi/rtmidi/RtMidi.h \
+           data/model/AggregateWaveModel.h \
+           data/model/AlignmentModel.h \
+           data/model/Dense3DModelPeakCache.h \
+           data/model/DenseThreeDimensionalModel.h \
+           data/model/DenseTimeValueModel.h \
+           data/model/EditableDenseThreeDimensionalModel.h \
+           data/model/FFTModel.h \
+           data/model/ImageModel.h \
+           data/model/IntervalModel.h \
+           data/model/Labeller.h \
+           data/model/Model.h \
+           data/model/ModelDataTableModel.h \
+           data/model/NoteModel.h \
+           data/model/FlexiNoteModel.h \
+           data/model/PathModel.h \
+           data/model/PowerOfSqrtTwoZoomConstraint.h \
+           data/model/PowerOfTwoZoomConstraint.h \
+           data/model/RangeSummarisableTimeValueModel.h \
+           data/model/RegionModel.h \
+           data/model/SparseModel.h \
+           data/model/SparseOneDimensionalModel.h \
+           data/model/SparseTimeValueModel.h \
+           data/model/SparseValueModel.h \
+           data/model/TabularModel.h \
+           data/model/TextModel.h \
+           data/model/WaveFileModel.h \
+           data/model/ReadOnlyWaveFileModel.h \
+           data/model/WritableWaveFileModel.h \
+           data/osc/OSCMessage.h \
+           data/osc/OSCQueue.h \
+	   plugin/PluginScan.h \
+           plugin/DSSIPluginFactory.h \
+           plugin/DSSIPluginInstance.h \
+           plugin/FeatureExtractionPluginFactory.h \
+           plugin/LADSPAPluginFactory.h \
+           plugin/LADSPAPluginInstance.h \
+           plugin/NativeVampPluginFactory.h \
+           plugin/PiperVampPluginFactory.h \
+           plugin/PluginIdentifier.h \
+           plugin/PluginXml.h \
+           plugin/RealTimePluginFactory.h \
+           plugin/RealTimePluginInstance.h \
+           plugin/api/dssi.h \
+           plugin/api/ladspa.h \
+           plugin/plugins/SamplePlayer.h \
+           plugin/api/alsa/asoundef.h \
+           plugin/api/alsa/asoundlib.h \
+           plugin/api/alsa/seq.h \
+           plugin/api/alsa/seq_event.h \
+           plugin/api/alsa/seq_midi_event.h \
+           plugin/api/alsa/sound/asequencer.h \
+	   rdf/PluginRDFIndexer.h \
+           rdf/PluginRDFDescription.h \
+           rdf/RDFExporter.h \
+           rdf/RDFFeatureWriter.h \
+           rdf/RDFImporter.h \
+           rdf/RDFTransformFactory.h \
+	   system/Init.h \
+           system/System.h \
+	   transform/CSVFeatureWriter.h \
+           transform/FeatureExtractionModelTransformer.h \
+           transform/FeatureWriter.h \
+           transform/FileFeatureWriter.h \
+           transform/RealTimeEffectModelTransformer.h \
+           transform/Transform.h \
+           transform/TransformDescription.h \
+           transform/TransformFactory.h \
+           transform/ModelTransformer.h \
+           transform/ModelTransformerFactory.h
+	   
+SVCORE_SOURCES = \
+           base/AudioLevel.cpp \
+           base/Clipboard.cpp \
+           base/Command.cpp \
+           base/Debug.cpp \
+           base/Exceptions.cpp \
+           base/LogRange.cpp \
+           base/Pitch.cpp \
+           base/PlayParameterRepository.cpp \
+           base/PlayParameters.cpp \
+           base/Preferences.cpp \
+           base/Profiler.cpp \
+           base/ProgressPrinter.cpp \
+           base/ProgressReporter.cpp \
+           base/PropertyContainer.cpp \
+           base/RangeMapper.cpp \
+           base/RealTimeSV.cpp \
+           base/RecentFiles.cpp \
+           base/ResamplerSV.cpp \
+           base/ResourceFinder.cpp \
+           base/Selection.cpp \
+           base/Serialiser.cpp \
+           base/StorageAdviser.cpp \
+           base/StringBits.cpp \
+           base/Strings.cpp \
+           base/TempDirectory.cpp \
+           base/TempWriteFile.cpp \
+           base/TextMatcher.cpp \
+           base/Thread.cpp \
+           base/UnitDatabase.cpp \
+           base/ViewManagerBase.cpp \
+           base/XmlExportable.cpp \
+	   data/fft/FFTapi.cpp \
+           data/fileio/AudioFileReader.cpp \
+           data/fileio/AudioFileReaderFactory.cpp \
+           data/fileio/AudioFileSizeEstimator.cpp \
+           data/fileio/BZipFileDevice.cpp \
+           data/fileio/CachedFile.cpp \
+           data/fileio/CodedAudioFileReader.cpp \
+           data/fileio/CSVFileReader.cpp \
+           data/fileio/CSVFileWriter.cpp \
+           data/fileio/CSVFormat.cpp \
+           data/fileio/DataFileReaderFactory.cpp \
+           data/fileio/FileReadThread.cpp \
+           data/fileio/FileSource.cpp \
+           data/fileio/MIDIFileReader.cpp \
+           data/fileio/MIDIFileWriter.cpp \
+           data/fileio/MP3FileReader.cpp \
+           data/fileio/OggVorbisFileReader.cpp \
+           data/fileio/PlaylistFileReader.cpp \
+           data/fileio/QuickTimeFileReader.cpp \
+           data/fileio/CoreAudioFileReader.cpp \
+           data/fileio/DecodingWavFileReader.cpp \
+           data/fileio/WavFileReader.cpp \
+           data/fileio/WavFileWriter.cpp \
+           data/midi/MIDIInput.cpp \
+           data/midi/rtmidi/RtMidi.cpp \
+           data/model/AggregateWaveModel.cpp \
+           data/model/AlignmentModel.cpp \
+           data/model/Dense3DModelPeakCache.cpp \
+           data/model/DenseTimeValueModel.cpp \
+           data/model/EditableDenseThreeDimensionalModel.cpp \
+           data/model/FFTModel.cpp \
+           data/model/Model.cpp \
+           data/model/ModelDataTableModel.cpp \
+           data/model/PowerOfSqrtTwoZoomConstraint.cpp \
+           data/model/PowerOfTwoZoomConstraint.cpp \
+           data/model/RangeSummarisableTimeValueModel.cpp \
+           data/model/WaveFileModel.cpp \
+           data/model/ReadOnlyWaveFileModel.cpp \
+           data/model/WritableWaveFileModel.cpp \
+           data/osc/OSCMessage.cpp \
+           data/osc/OSCQueue.cpp \
+	   plugin/PluginScan.cpp \
+           plugin/DSSIPluginFactory.cpp \
+           plugin/DSSIPluginInstance.cpp \
+           plugin/FeatureExtractionPluginFactory.cpp \
+           plugin/LADSPAPluginFactory.cpp \
+           plugin/LADSPAPluginInstance.cpp \
+           plugin/NativeVampPluginFactory.cpp \
+           plugin/PiperVampPluginFactory.cpp \
+           plugin/PluginIdentifier.cpp \
+           plugin/PluginXml.cpp \
+           plugin/RealTimePluginFactory.cpp \
+           plugin/RealTimePluginInstance.cpp \
+           plugin/plugins/SamplePlayer.cpp \
+	   rdf/PluginRDFIndexer.cpp \
+           rdf/PluginRDFDescription.cpp \
+           rdf/RDFExporter.cpp \
+           rdf/RDFFeatureWriter.cpp \
+           rdf/RDFImporter.cpp \
+           rdf/RDFTransformFactory.cpp \
+	   system/Init.cpp \
+           system/System.cpp \
+	   transform/CSVFeatureWriter.cpp \
+           transform/FeatureExtractionModelTransformer.cpp \
+           transform/FileFeatureWriter.cpp \
+           transform/RealTimeEffectModelTransformer.cpp \
+           transform/Transform.cpp \
+           transform/TransformFactory.cpp \
+           transform/ModelTransformer.cpp \
+           transform/ModelTransformerFactory.cpp
+
+!linux* {
+    SVCORE_SOURCES += plugin/api/dssi_alsa_compat.c 
+}
+
--- a/plugin/PiperVampPluginFactory.cpp	Tue Oct 25 10:53:12 2016 +0100
+++ b/plugin/PiperVampPluginFactory.cpp	Fri Oct 28 15:19:12 2016 +0100
@@ -53,7 +53,7 @@
     // (preferably) a subdirectory called "piper-bin".
     //!!! todo: merge this with plugin scan checker thingy used in main.cpp?
     QString myDir = QCoreApplication::applicationDirPath();
-    QString name = "piper-vamp-server";
+    QString name = "piper-vamp-simple-server";
     QString path = myDir + "/piper-bin/" + name;
     QString suffix = "";
 #ifdef _WIN32
@@ -143,14 +143,26 @@
 {
     if (m_serverName == "") return;
 
-    piper_vamp::client::ProcessQtTransport transport(m_serverName);
+    piper_vamp::client::ProcessQtTransport transport(m_serverName, "capnp");
     if (!transport.isOK()) {
         errorMessage = QObject::tr("Could not start external plugin host");
         return;
     }
-            
+
     piper_vamp::client::CapnpRRClient client(&transport);
-    piper_vamp::ListResponse lr = client.listPluginData();
+    piper_vamp::ListResponse lr;
+
+    try {
+        lr = client.listPluginData();
+    } catch (piper_vamp::client::ServerCrashed) {
+        errorMessage = QObject::tr
+            ("External plugin host exited unexpectedly while listing plugins");
+        return;
+    } catch (const std::exception &e) {
+        errorMessage = QObject::tr("External plugin host invocation failed: %1")
+            .arg(e.what());
+        return;
+    }
 
     for (const auto &pd: lr.available) {
 
--- a/plugin/PluginScan.cpp	Tue Oct 25 10:53:12 2016 +0100
+++ b/plugin/PluginScan.cpp	Fri Oct 28 15:19:12 2016 +0100
@@ -23,10 +23,15 @@
 
 using std::string;
 
+//#define DEBUG_PLUGIN_SCAN 1
+
 class PluginScan::Logger : public PluginCandidates::LogCallback
 {
 protected:
     void log(std::string message) {
+#ifdef DEBUG_PLUGIN_SCAN
+        cerr << "PluginScan: " << message;
+#endif
         SVDEBUG << "PluginScan: " << message;
     }
 };
--- a/svcore.pro	Tue Oct 25 10:53:12 2016 +0100
+++ b/svcore.pro	Fri Oct 28 15:19:12 2016 +0100
@@ -2,41 +2,10 @@
 TEMPLATE = lib
 
 INCLUDEPATH += ../vamp-plugin-sdk
-DEFINES += HAVE_VAMP HAVE_VAMPHOSTSDK
 
 exists(config.pri) {
     include(config.pri)
 }
-!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* {
-        # We actually expect MSVC to be used only for 64-bit builds,
-        # though the qmake spec is still called win32-msvc*
-        INCLUDEPATH += ../sv-dependency-builds/win64-msvc/include
-        LIBS += -L../sv-dependency-builds/win64-msvc/lib
-    }
-    macx* {
-        INCLUDEPATH += ../sv-dependency-builds/osx/include
-        LIBS += -L../sv-dependency-builds/osx/lib
-    }
-
-    DEFINES += HAVE_BZ2 HAVE_FFTW3 HAVE_FFTW3F HAVE_SNDFILE HAVE_SAMPLERATE HAVE_LIBLO HAVE_MAD HAVE_ID3TAG 
-
-    macx* {
-        DEFINES += HAVE_COREAUDIO
-    }
-    win32-msvc* {
-        DEFINES += NOMINMAX _USE_MATH_DEFINES
-        DEFINES -= HAVE_LIBLO
-    }
-}
 
 CONFIG += staticlib qt thread warn_on stl rtti exceptions c++11
 QT += network xml
@@ -58,253 +27,8 @@
 win*:     DEFINES += __WINDOWS_MM__
 solaris*: DEFINES += __RTMIDI_DUMMY_ONLY__
 
-HEADERS += base/AudioLevel.h \
-           base/AudioPlaySource.h \
-           base/BaseTypes.h \
-           base/Clipboard.h \
-           base/ColumnOp.h \
-           base/Command.h \
-           base/Debug.h \
-           base/Exceptions.h \
-           base/LogRange.h \
-           base/MagnitudeRange.h \
-           base/Pitch.h \
-           base/Playable.h \
-           base/PlayParameterRepository.h \
-           base/PlayParameters.h \
-           base/Preferences.h \
-           base/Profiler.h \
-           base/ProgressPrinter.h \
-           base/ProgressReporter.h \
-           base/PropertyContainer.h \
-           base/RangeMapper.h \
-           base/RealTime.h \
-           base/RecentFiles.h \
-           base/Resampler.h \
-           base/ResourceFinder.h \
-           base/RingBuffer.h \
-           base/Scavenger.h \
-           base/Selection.h \
-           base/Serialiser.h \
-           base/StorageAdviser.h \
-           base/StringBits.h \
-           base/Strings.h \
-           base/TempDirectory.h \
-           base/TempWriteFile.h \
-           base/TextMatcher.h \
-           base/Thread.h \
-           base/UnitDatabase.h \
-           base/ViewManagerBase.h \
-           base/Window.h \
-           base/XmlExportable.h \
-           base/ZoomConstraint.h
-SOURCES += base/AudioLevel.cpp \
-           base/Clipboard.cpp \
-           base/Command.cpp \
-           base/Debug.cpp \
-           base/Exceptions.cpp \
-           base/LogRange.cpp \
-           base/Pitch.cpp \
-           base/PlayParameterRepository.cpp \
-           base/PlayParameters.cpp \
-           base/Preferences.cpp \
-           base/Profiler.cpp \
-           base/ProgressPrinter.cpp \
-           base/ProgressReporter.cpp \
-           base/PropertyContainer.cpp \
-           base/RangeMapper.cpp \
-           base/RealTime.cpp \
-           base/RecentFiles.cpp \
-           base/Resampler.cpp \
-           base/ResourceFinder.cpp \
-           base/Selection.cpp \
-           base/Serialiser.cpp \
-           base/StorageAdviser.cpp \
-           base/StringBits.cpp \
-           base/Strings.cpp \
-           base/TempDirectory.cpp \
-           base/TempWriteFile.cpp \
-           base/TextMatcher.cpp \
-           base/Thread.cpp \
-           base/UnitDatabase.cpp \
-           base/ViewManagerBase.cpp \
-           base/XmlExportable.cpp
+include(files.pri)
 
-HEADERS += data/fft/FFTapi.h \
-           data/fileio/AudioFileReader.h \
-           data/fileio/AudioFileReaderFactory.h \
-           data/fileio/AudioFileSizeEstimator.h \
-           data/fileio/BZipFileDevice.h \
-           data/fileio/CachedFile.h \
-           data/fileio/CodedAudioFileReader.h \
-           data/fileio/CSVFileReader.h \
-           data/fileio/CSVFileWriter.h \
-           data/fileio/CSVFormat.h \
-           data/fileio/DataFileReader.h \
-           data/fileio/DataFileReaderFactory.h \
-           data/fileio/FileFinder.h \
-           data/fileio/FileReadThread.h \
-           data/fileio/FileSource.h \
-           data/fileio/MIDIFileReader.h \
-           data/fileio/MIDIFileWriter.h \
-           data/fileio/MP3FileReader.h \
-           data/fileio/OggVorbisFileReader.h \
-           data/fileio/PlaylistFileReader.h \
-           data/fileio/QuickTimeFileReader.h \
-           data/fileio/CoreAudioFileReader.h \
-           data/fileio/DecodingWavFileReader.h \
-           data/fileio/WavFileReader.h \
-           data/fileio/WavFileWriter.h \
-           data/midi/MIDIEvent.h \
-           data/midi/MIDIInput.h \
-           data/midi/rtmidi/RtError.h \
-           data/midi/rtmidi/RtMidi.h \
-           data/model/AggregateWaveModel.h \
-           data/model/AlignmentModel.h \
-           data/model/Dense3DModelPeakCache.h \
-           data/model/DenseThreeDimensionalModel.h \
-           data/model/DenseTimeValueModel.h \
-           data/model/EditableDenseThreeDimensionalModel.h \
-           data/model/FFTModel.h \
-           data/model/ImageModel.h \
-           data/model/IntervalModel.h \
-           data/model/Labeller.h \
-           data/model/Model.h \
-           data/model/ModelDataTableModel.h \
-           data/model/NoteModel.h \
-           data/model/FlexiNoteModel.h \
-           data/model/PathModel.h \
-           data/model/PowerOfSqrtTwoZoomConstraint.h \
-           data/model/PowerOfTwoZoomConstraint.h \
-           data/model/RangeSummarisableTimeValueModel.h \
-           data/model/RegionModel.h \
-           data/model/SparseModel.h \
-           data/model/SparseOneDimensionalModel.h \
-           data/model/SparseTimeValueModel.h \
-           data/model/SparseValueModel.h \
-           data/model/TabularModel.h \
-           data/model/TextModel.h \
-           data/model/WaveFileModel.h \
-           data/model/ReadOnlyWaveFileModel.h \
-           data/model/WritableWaveFileModel.h \
-           data/osc/OSCMessage.h \
-           data/osc/OSCQueue.h 
-SOURCES += data/fft/FFTapi.cpp \
-           data/fileio/AudioFileReader.cpp \
-           data/fileio/AudioFileReaderFactory.cpp \
-           data/fileio/AudioFileSizeEstimator.cpp \
-           data/fileio/BZipFileDevice.cpp \
-           data/fileio/CachedFile.cpp \
-           data/fileio/CodedAudioFileReader.cpp \
-           data/fileio/CSVFileReader.cpp \
-           data/fileio/CSVFileWriter.cpp \
-           data/fileio/CSVFormat.cpp \
-           data/fileio/DataFileReaderFactory.cpp \
-           data/fileio/FileReadThread.cpp \
-           data/fileio/FileSource.cpp \
-           data/fileio/MIDIFileReader.cpp \
-           data/fileio/MIDIFileWriter.cpp \
-           data/fileio/MP3FileReader.cpp \
-           data/fileio/OggVorbisFileReader.cpp \
-           data/fileio/PlaylistFileReader.cpp \
-           data/fileio/QuickTimeFileReader.cpp \
-           data/fileio/CoreAudioFileReader.cpp \
-           data/fileio/DecodingWavFileReader.cpp \
-           data/fileio/WavFileReader.cpp \
-           data/fileio/WavFileWriter.cpp \
-           data/midi/MIDIInput.cpp \
-           data/midi/rtmidi/RtMidi.cpp \
-           data/model/AggregateWaveModel.cpp \
-           data/model/AlignmentModel.cpp \
-           data/model/Dense3DModelPeakCache.cpp \
-           data/model/DenseTimeValueModel.cpp \
-           data/model/EditableDenseThreeDimensionalModel.cpp \
-           data/model/FFTModel.cpp \
-           data/model/Model.cpp \
-           data/model/ModelDataTableModel.cpp \
-           data/model/PowerOfSqrtTwoZoomConstraint.cpp \
-           data/model/PowerOfTwoZoomConstraint.cpp \
-           data/model/RangeSummarisableTimeValueModel.cpp \
-           data/model/WaveFileModel.cpp \
-           data/model/ReadOnlyWaveFileModel.cpp \
-           data/model/WritableWaveFileModel.cpp \
-           data/osc/OSCMessage.cpp \
-           data/osc/OSCQueue.cpp 
+HEADERS = $$(SVCORE_HEADERS)
+SOURCES = $$(SVCORE_SOURCES)
 
-HEADERS += plugin/PluginScan.h \
-           plugin/DSSIPluginFactory.h \
-           plugin/DSSIPluginInstance.h \
-           plugin/FeatureExtractionPluginFactory.h \
-           plugin/LADSPAPluginFactory.h \
-           plugin/LADSPAPluginInstance.h \
-           plugin/NativeVampPluginFactory.h \
-           plugin/PiperVampPluginFactory.h \
-           plugin/PluginIdentifier.h \
-           plugin/PluginXml.h \
-           plugin/RealTimePluginFactory.h \
-           plugin/RealTimePluginInstance.h \
-           plugin/api/dssi.h \
-           plugin/api/ladspa.h \
-           plugin/plugins/SamplePlayer.h \
-           plugin/api/alsa/asoundef.h \
-           plugin/api/alsa/asoundlib.h \
-           plugin/api/alsa/seq.h \
-           plugin/api/alsa/seq_event.h \
-           plugin/api/alsa/seq_midi_event.h \
-           plugin/api/alsa/sound/asequencer.h
-
-
-SOURCES += plugin/PluginScan.cpp \
-           plugin/DSSIPluginFactory.cpp \
-           plugin/DSSIPluginInstance.cpp \
-           plugin/FeatureExtractionPluginFactory.cpp \
-           plugin/LADSPAPluginFactory.cpp \
-           plugin/LADSPAPluginInstance.cpp \
-           plugin/NativeVampPluginFactory.cpp \
-           plugin/PiperVampPluginFactory.cpp \
-           plugin/PluginIdentifier.cpp \
-           plugin/PluginXml.cpp \
-           plugin/RealTimePluginFactory.cpp \
-           plugin/RealTimePluginInstance.cpp \
-           plugin/plugins/SamplePlayer.cpp
-
-!linux* {
-SOURCES += plugin/api/dssi_alsa_compat.c 
-}
-
-HEADERS += rdf/PluginRDFIndexer.h \
-           rdf/PluginRDFDescription.h \
-           rdf/RDFExporter.h \
-           rdf/RDFFeatureWriter.h \
-           rdf/RDFImporter.h \
-           rdf/RDFTransformFactory.h
-SOURCES += rdf/PluginRDFIndexer.cpp \
-           rdf/PluginRDFDescription.cpp \
-           rdf/RDFExporter.cpp \
-           rdf/RDFFeatureWriter.cpp \
-           rdf/RDFImporter.cpp \
-           rdf/RDFTransformFactory.cpp
-
-HEADERS += system/Init.h \
-           system/System.h
-SOURCES += system/Init.cpp \
-           system/System.cpp
-
-HEADERS += transform/CSVFeatureWriter.h \
-           transform/FeatureExtractionModelTransformer.h \
-           transform/FeatureWriter.h \
-           transform/FileFeatureWriter.h \
-           transform/RealTimeEffectModelTransformer.h \
-           transform/Transform.h \
-           transform/TransformDescription.h \
-           transform/TransformFactory.h \
-           transform/ModelTransformer.h \
-           transform/ModelTransformerFactory.h
-SOURCES += transform/CSVFeatureWriter.cpp \
-           transform/FeatureExtractionModelTransformer.cpp \
-           transform/FileFeatureWriter.cpp \
-           transform/RealTimeEffectModelTransformer.cpp \
-           transform/Transform.cpp \
-           transform/TransformFactory.cpp \
-           transform/ModelTransformer.cpp \
-           transform/ModelTransformerFactory.cpp
--- a/system/System.h	Tue Oct 25 10:53:12 2016 +0100
+++ b/system/System.h	Fri Oct 28 15:19:12 2016 +0100
@@ -64,12 +64,15 @@
 typedef SSIZE_T ssize_t;
 #endif
 
+#ifdef _MSC_VER
 extern "C" {
-
-#ifdef _MSC_VER
 void usleep(unsigned long usec);
+}
+#else
+#include <unistd.h>
 #endif
 
+extern "C" {
 int gettimeofday(struct timeval *p, void *tz);
 }
 
--- a/transform/FeatureExtractionModelTransformer.cpp	Tue Oct 25 10:53:12 2016 +0100
+++ b/transform/FeatureExtractionModelTransformer.cpp	Fri Oct 28 15:19:12 2016 +0100
@@ -47,8 +47,6 @@
     m_haveOutputs(false)
 {
     SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: plugin " << m_transforms.begin()->getPluginIdentifier() << ", outputName " << m_transforms.begin()->getOutput() << endl;
-
-//    initialise();
 }
 
 FeatureExtractionModelTransformer::FeatureExtractionModelTransformer(Input in,
@@ -62,8 +60,6 @@
     } else {
         SVDEBUG << "FeatureExtractionModelTransformer::FeatureExtractionModelTransformer: " << transforms.size() << " transform(s), first has plugin " << m_transforms.begin()->getPluginIdentifier() << ", outputName " << m_transforms.begin()->getOutput() << endl;
     }
-    
-//    initialise();
 }
 
 static bool
@@ -77,6 +73,10 @@
 bool
 FeatureExtractionModelTransformer::initialise()
 {
+    // This is (now) called from the run thread. The plugin is
+    // constructed, initialised, used, and destroyed all from a single
+    // thread.
+    
     // All transforms must use the same plugin, parameters, and
     // inputs: they can differ only in choice of plugin output. So we
     // initialise based purely on the first transform in the list (but
@@ -235,6 +235,18 @@
 }
 
 void
+FeatureExtractionModelTransformer::deinitialise()
+{
+    cerr << "deleting plugin for transform in thread "
+         << QThread::currentThreadId() << endl;
+    
+    delete m_plugin;
+    for (int j = 0; j < (int)m_descriptors.size(); ++j) {
+        delete m_descriptors[j];
+    }
+}
+
+void
 FeatureExtractionModelTransformer::createOutputModels(int n)
 {
     DenseTimeValueModel *input = getConformingInput();
@@ -502,11 +514,9 @@
 
 FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()
 {
-//    SVDEBUG << "FeatureExtractionModelTransformer::~FeatureExtractionModelTransformer()" << endl;
-    delete m_plugin;
-    for (int j = 0; j < (int)m_descriptors.size(); ++j) {
-        delete m_descriptors[j];
-    }
+    // Parent class dtor set the abandoned flag and waited for the run
+    // thread to exit; the run thread owns the plugin, and should have
+    // destroyed it before exiting (via a call to deinitialise)
 }
 
 FeatureExtractionModelTransformer::Models
@@ -781,6 +791,8 @@
         delete[] buffers[ch];
     }
     delete[] buffers;
+
+    deinitialise();
 }
 
 void
--- a/transform/FeatureExtractionModelTransformer.h	Tue Oct 25 10:53:12 2016 +0100
+++ b/transform/FeatureExtractionModelTransformer.h	Fri Oct 28 15:19:12 2016 +0100
@@ -30,7 +30,7 @@
 class DenseTimeValueModel;
 class SparseTimeValueModel;
 
-class FeatureExtractionModelTransformer : public ModelTransformer
+class FeatureExtractionModelTransformer : public ModelTransformer // + is a Thread
 {
     Q_OBJECT
 
@@ -52,6 +52,7 @@
 
 protected:
     bool initialise();
+    void deinitialise();
 
     virtual void run();