changeset 1096:4d9816ba0ebe simple-fft-model

Rework audio file reader API to prefer using std containers
author Chris Cannam
date Mon, 15 Jun 2015 12:19:47 +0100
parents b66734b5f806
children abc309f507ae
files data/fileio/AudioFileReader.cpp data/fileio/AudioFileReader.h data/fileio/CodedAudioFileReader.cpp data/fileio/CodedAudioFileReader.h data/fileio/DecodingWavFileReader.cpp data/fileio/DecodingWavFileReader.h data/fileio/WavFileReader.cpp data/fileio/WavFileReader.h data/fileio/WavFileWriter.cpp data/model/AggregateWaveModel.cpp data/model/AggregateWaveModel.h data/model/DenseTimeValueModel.cpp data/model/DenseTimeValueModel.h data/model/FFTModel.cpp data/model/Model.h data/model/WaveFileModel.cpp data/model/WaveFileModel.h data/model/WritableWaveFileModel.cpp data/model/WritableWaveFileModel.h data/model/test/MockWaveModel.cpp data/model/test/MockWaveModel.h data/model/test/test.pro transform/FeatureExtractionModelTransformer.cpp transform/RealTimeEffectModelTransformer.cpp
diffstat 24 files changed, 241 insertions(+), 309 deletions(-) [+]
line wrap: on
line diff
--- a/data/fileio/AudioFileReader.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/AudioFileReader.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -17,15 +17,17 @@
 
 using std::vector;
 
-vector<SampleBlock>
+vector<vector<float>>
 AudioFileReader::getDeInterleavedFrames(sv_frame_t start, sv_frame_t count) const
 {
-    SampleBlock interleaved = getInterleavedFrames(start, count);
+    vector<float> interleaved = getInterleavedFrames(start, count);
     
     int channels = getChannelCount();
+    if (channels == 1) return { interleaved };
+    
     sv_frame_t rc = interleaved.size() / channels;
 
-    vector<SampleBlock> frames(channels, SampleBlock(rc, 0.f));
+    vector<vector<float>> frames(channels, vector<float>(rc, 0.f));
     
     for (int c = 0; c < channels; ++c) {
         for (sv_frame_t i = 0; i < rc; ++i) {
--- a/data/fileio/AudioFileReader.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/AudioFileReader.h	Mon Jun 15 12:19:47 2015 +0100
@@ -24,8 +24,6 @@
 #include <vector>
 #include <map>
 
-typedef std::vector<float> SampleBlock;
-
 class AudioFileReader : public QObject
 {
     Q_OBJECT
@@ -85,16 +83,14 @@
 
     /** 
      * Return interleaved samples for count frames from index start.
-     * The resulting sample block will contain count *
-     * getChannelCount() samples (or fewer if end of file is
-     * reached). The caller does not need to allocate space and any
-     * existing content in the SampleBlock will be erased.
+     * The resulting vector will contain count * getChannelCount()
+     * samples (or fewer if end of file is reached).
      *
      * The subclass implementations of this function must be
      * thread-safe -- that is, safe to call from multiple threads with
      * different arguments on the same object at the same time.
      */
-    virtual SampleBlock getInterleavedFrames(sv_frame_t start, sv_frame_t count) const = 0;
+    virtual std::vector<float> getInterleavedFrames(sv_frame_t start, sv_frame_t count) const = 0;
 
     /**
      * Return de-interleaved samples for count frames from index
@@ -103,7 +99,7 @@
      * will contain getChannelCount() sample blocks of count samples
      * each (or fewer if end of file is reached).
      */
-    virtual std::vector<SampleBlock> getDeInterleavedFrames(sv_frame_t start, sv_frame_t count) const;
+    virtual std::vector<std::vector<float> > getDeInterleavedFrames(sv_frame_t start, sv_frame_t count) const;
 
     // only subclasses that do not know exactly how long the audio
     // file is until it's been completely decoded should implement this
--- a/data/fileio/CodedAudioFileReader.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/CodedAudioFileReader.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -27,6 +27,8 @@
 #include <QDir>
 #include <QMutexLocker>
 
+using namespace std;
+
 CodedAudioFileReader::CodedAudioFileReader(CacheMode cacheMode,
                                            sv_samplerate_t targetRate,
                                            bool normalised) :
@@ -242,7 +244,7 @@
 }
 
 void
-CodedAudioFileReader::addSamplesToDecodeCache(const SampleBlock &samples)
+CodedAudioFileReader::addSamplesToDecodeCache(const vector<float> &samples)
 {
     QMutexLocker locker(&m_cacheMutex);
 
@@ -352,9 +354,7 @@
 
     case CacheInMemory:
         m_dataLock.lockForWrite();
-        for (sv_frame_t s = 0; s < count; ++s) {
-            m_data.push_back(buffer[s]);
-        }
+        m_data.insert(m_data.end(), buffer, buffer + count);
         m_dataLock.unlock();
         break;
     }
@@ -408,7 +408,7 @@
     }
 }
 
-SampleBlock
+vector<float>
 CodedAudioFileReader::getInterleavedFrames(sv_frame_t start, sv_frame_t count) const
 {
     // Lock is only required in CacheInMemory mode (the cache file
@@ -417,10 +417,10 @@
 
     if (!m_initialised) {
         SVDEBUG << "CodedAudioFileReader::getInterleavedFrames: not initialised" << endl;
-        return SampleBlock();
+        return {};
     }
 
-    SampleBlock frames;
+    vector<float> frames;
     
     switch (m_cacheMode) {
 
@@ -432,8 +432,8 @@
 
     case CacheInMemory:
     {
-        if (!isOK()) return SampleBlock();
-        if (count == 0) return SampleBlock();
+        if (!isOK()) return {};
+        if (count == 0) return {};
 
         sv_frame_t idx = start * m_channelCount;
         sv_frame_t i = 0;
--- a/data/fileio/CodedAudioFileReader.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/CodedAudioFileReader.h	Mon Jun 15 12:19:47 2015 +0100
@@ -38,7 +38,7 @@
         CacheInMemory
     };
 
-    virtual SampleBlock getInterleavedFrames(sv_frame_t start, sv_frame_t count) const;
+    virtual std::vector<float> getInterleavedFrames(sv_frame_t start, sv_frame_t count) const;
 
     virtual sv_samplerate_t getNativeRate() const { return m_fileRate; }
 
@@ -60,7 +60,7 @@
     // may throw InsufficientDiscSpace:
     void addSamplesToDecodeCache(float **samples, sv_frame_t nframes);
     void addSamplesToDecodeCache(float *samplesInterleaved, sv_frame_t nframes);
-    void addSamplesToDecodeCache(const SampleBlock &interleaved);
+    void addSamplesToDecodeCache(const std::vector<float> &interleaved);
 
     // may throw InsufficientDiscSpace:
     void finishDecodeCache();
@@ -78,7 +78,7 @@
 protected:
     QMutex m_cacheMutex;
     CacheMode m_cacheMode;
-    SampleBlock m_data;
+    std::vector<float> m_data;
     mutable QReadWriteLock m_dataLock;
     bool m_initialised;
     Serialiser *m_serialiser;
--- a/data/fileio/DecodingWavFileReader.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/DecodingWavFileReader.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -21,6 +21,8 @@
 
 #include <QFileInfo>
 
+using namespace std;
+
 DecodingWavFileReader::DecodingWavFileReader(FileSource source,
                                              ResampleMode resampleMode,
                                              CacheMode mode,
@@ -67,7 +69,7 @@
         sv_frame_t blockSize = 16384;
         sv_frame_t total = m_original->getFrameCount();
 
-        SampleBlock block;
+        vector<float> block;
 
         for (sv_frame_t i = 0; i < total; i += blockSize) {
 
@@ -124,7 +126,7 @@
     sv_frame_t blockSize = 16384;
     sv_frame_t total = m_reader->m_original->getFrameCount();
     
-    SampleBlock block;
+    vector<float> block;
     
     for (sv_frame_t i = 0; i < total; i += blockSize) {
         
@@ -147,7 +149,7 @@
 } 
 
 void
-DecodingWavFileReader::addBlock(const SampleBlock &frames)
+DecodingWavFileReader::addBlock(const vector<float> &frames)
 {
     addSamplesToDecodeCache(frames);
 
@@ -167,7 +169,7 @@
 }
 
 void
-DecodingWavFileReader::getSupportedExtensions(std::set<QString> &extensions)
+DecodingWavFileReader::getSupportedExtensions(set<QString> &extensions)
 {
     WavFileReader::getSupportedExtensions(extensions);
 }
--- a/data/fileio/DecodingWavFileReader.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/DecodingWavFileReader.h	Mon Jun 15 12:19:47 2015 +0100
@@ -69,7 +69,7 @@
     WavFileReader *m_original;
     ProgressReporter *m_reporter;
 
-    void addBlock(const SampleBlock &frames);
+    void addBlock(const std::vector<float> &frames);
     
     class DecodeThread : public Thread
     {
--- a/data/fileio/WavFileReader.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/WavFileReader.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -20,6 +20,8 @@
 #include <QMutexLocker>
 #include <QFileInfo>
 
+using namespace std;
+
 WavFileReader::WavFileReader(FileSource source, bool fileUpdating) :
     m_file(0),
     m_source(source),
@@ -119,53 +121,56 @@
     m_updating = false;
 }
 
-SampleBlock
+vector<float>
 WavFileReader::getInterleavedFrames(sv_frame_t start, sv_frame_t count) const
 {
-    if (count == 0) return SampleBlock();
+    if (count == 0) return {};
 
     QMutexLocker locker(&m_mutex);
 
     if (!m_file || !m_channelCount) {
-        return SampleBlock();
+        return {};
     }
 
     if (start >= m_fileInfo.frames) {
 //        SVDEBUG << "WavFileReader::getInterleavedFrames: " << start
 //                  << " > " << m_fileInfo.frames << endl;
-	return SampleBlock();
+	return {};
     }
 
     if (start + count > m_fileInfo.frames) {
 	count = m_fileInfo.frames - start;
     }
 
-    if (start != m_lastStart || count != m_lastCount) {
-
-	if (sf_seek(m_file, start, SEEK_SET) < 0) {
-	    return SampleBlock();
-	}
-
-        sv_frame_t n = count * m_fileInfo.channels;
-        m_buffer.resize(n);
-	
-        sf_count_t readCount = 0;
-
-	if ((readCount = sf_readf_float(m_file, m_buffer.data(), count)) < 0) {
-	    return SampleBlock();
-	}
-
-        m_buffer.resize(readCount * m_fileInfo.channels);
-        
-	m_lastStart = start;
-	m_lastCount = readCount;
+    // Because WaveFileModel::getSummaries() is called separately for
+    // individual channels, it's quite common for us to be called
+    // repeatedly for the same data. So this is worth cacheing.
+    if (start == m_lastStart && count == m_lastCount) {
+        return m_buffer;
+    }
+    
+    if (sf_seek(m_file, start, SEEK_SET) < 0) {
+        return {};
     }
 
-    return m_buffer;
+    vector<float> data;
+    sv_frame_t n = count * m_fileInfo.channels;
+    data.resize(n);
+
+    m_buffer = data;
+    m_lastStart = start;
+    m_lastCount = count;
+    
+    sf_count_t readCount = 0;
+    if ((readCount = sf_readf_float(m_file, data.data(), count)) < 0) {
+        return {};
+    }
+
+    return data;
 }
 
 void
-WavFileReader::getSupportedExtensions(std::set<QString> &extensions)
+WavFileReader::getSupportedExtensions(set<QString> &extensions)
 {
     int count;
 
@@ -196,7 +201,7 @@
 bool
 WavFileReader::supportsExtension(QString extension)
 {
-    std::set<QString> extensions;
+    set<QString> extensions;
     getSupportedExtensions(extensions);
     return (extensions.find(extension.toLower()) != extensions.end());
 }
--- a/data/fileio/WavFileReader.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/WavFileReader.h	Mon Jun 15 12:19:47 2015 +0100
@@ -50,7 +50,7 @@
      * Must be safe to call from multiple threads with different
      * arguments on the same object at the same time.
      */
-    virtual SampleBlock getInterleavedFrames(sv_frame_t start, sv_frame_t count) const;
+    virtual std::vector<float> getInterleavedFrames(sv_frame_t start, sv_frame_t count) const;
     
     static void getSupportedExtensions(std::set<QString> &extensions);
     static bool supportsExtension(QString ext);
@@ -75,8 +75,7 @@
     bool m_seekable;
 
     mutable QMutex m_mutex;
-    mutable SampleBlock m_buffer;
-    mutable sv_frame_t m_bufsiz;
+    mutable std::vector<float> m_buffer;
     mutable sv_frame_t m_lastStart;
     mutable sv_frame_t m_lastCount;
 
--- a/data/fileio/WavFileWriter.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/fileio/WavFileWriter.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -25,6 +25,8 @@
 #include <iostream>
 #include <cmath>
 
+using namespace std;
+
 WavFileWriter::WavFileWriter(QString path,
 			     sv_samplerate_t sampleRate,
                              int channels,
@@ -129,8 +131,6 @@
     }
 
     sv_frame_t bs = 2048;
-    float *ub = new float[bs]; // uninterleaved buffer (one channel)
-    float *ib = new float[bs * m_channels]; // interleaved buffer
 
     for (MultiSelection::SelectionList::iterator i =
 	     selection->getSelections().begin();
@@ -140,16 +140,17 @@
 
 	for (sv_frame_t f = f0; f < f1; f += bs) {
 	    
-	    sv_frame_t n = std::min(bs, f1 - f);
+	    sv_frame_t n = min(bs, f1 - f);
+            vector<float> interleaved(n * m_channels, 0.f);
 
 	    for (int c = 0; c < int(m_channels); ++c) {
-		source->getData(c, f, n, ub);
-		for (int i = 0; i < n; ++i) {
-		    ib[i * m_channels + c] = ub[i];
+                vector<float> chanbuf = source->getData(c, f, n);
+		for (int i = 0; in_range_for(chanbuf, i); ++i) {
+		    interleaved[i * m_channels + c] = chanbuf[i];
 		}
 	    }	    
 
-	    sf_count_t written = sf_writef_float(m_file, ib, n);
+	    sf_count_t written = sf_writef_float(m_file, interleaved.data(), n);
 
 	    if (written < n) {
 		m_error = QString("Only wrote %1 of %2 frames at file frame %3")
@@ -159,8 +160,6 @@
 	}
     }
 
-    delete[] ub;
-    delete[] ib;
     if (ownSelection) delete selection;
 
     return isOK();
--- a/data/model/AggregateWaveModel.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/AggregateWaveModel.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -19,6 +19,8 @@
 
 #include <QTextStream>
 
+using namespace std;
+
 PowerOfSqrtTwoZoomConstraint
 AggregateWaveModel::m_zoomConstraint;
 
@@ -92,65 +94,54 @@
     return m_components.begin()->model->getSampleRate();
 }
 
-sv_frame_t
-AggregateWaveModel::getData(int channel, sv_frame_t start, sv_frame_t count,
-                            float *buffer) const
+vector<float>
+AggregateWaveModel::getData(int channel, sv_frame_t start, sv_frame_t count) const
 {
     int ch0 = channel, ch1 = channel;
-    bool mixing = false;
     if (channel == -1) {
         ch0 = 0;
         ch1 = getChannelCount()-1;
-        mixing = true;
     }
 
-    float *readbuf = buffer;
-    if (mixing) {
-        readbuf = new float[count];
-        for (sv_frame_t i = 0; i < count; ++i) {
-            buffer[i] = 0.f;
-        }
-    }
+    vector<float> result(count, 0.f);
 
     sv_frame_t longest = 0;
     
     for (int c = ch0; c <= ch1; ++c) {
-        sv_frame_t here = 
-            m_components[c].model->getData(m_components[c].channel,
-                                           start, count,
-                                           readbuf);
-        if (here > longest) {
-            longest = here;
+
+        auto here = m_components[c].model->getData(m_components[c].channel,
+                                                   start, count);
+        if (here.size() > longest) {
+            longest = here.size();
         }
-        if (here < count) {
-            for (sv_frame_t i = here; i < count; ++i) {
-                readbuf[i] = 0.f;
-            }
-        }
-        if (mixing) {
-            for (sv_frame_t i = 0; i < count; ++i) {
-                buffer[i] += readbuf[i];
-            }
+        for (sv_frame_t i = 0; in_range_for(here, i); ++i) {
+            result[i] += here[i];
         }
     }
 
-    if (mixing) delete[] readbuf;
-    return longest;
+    result.resize(longest);
+    return result;
 }
 
-sv_frame_t
+vector<vector<float>>
 AggregateWaveModel::getMultiChannelData(int fromchannel, int tochannel,
-                                        sv_frame_t start, sv_frame_t count,
-                                        float **buffer) const
+                                        sv_frame_t start, sv_frame_t count) const
 {
     sv_frame_t min = count;
 
+    vector<vector<float>> result;
+
     for (int c = fromchannel; c <= tochannel; ++c) {
-        sv_frame_t here = getData(c, start, count, buffer[c - fromchannel]);
-        if (here < min) min = here;
+        auto here = getData(c, start, count);
+        if (here.size() < min) min = here.size();
+        result.push_back(here);
+    }
+
+    if (min < count) {
+        for (auto &v : result) v.resize(min);
     }
     
-    return min;
+    return result;
 }
 
 int
--- a/data/model/AggregateWaveModel.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/AggregateWaveModel.h	Mon Jun 15 12:19:47 2015 +0100
@@ -59,12 +59,9 @@
     virtual sv_frame_t getStartFrame() const { return 0; }
     virtual sv_frame_t getEndFrame() const { return getFrameCount(); }
 
-    virtual sv_frame_t getData(int channel, sv_frame_t start, sv_frame_t count,
-                               float *buffer) const;
+    virtual std::vector<float> getData(int channel, sv_frame_t start, sv_frame_t count) const;
 
-    virtual sv_frame_t getMultiChannelData(int fromchannel, int tochannel,
-                                           sv_frame_t start, sv_frame_t count,
-                                           float **buffer) const;
+    virtual std::vector<std::vector<float>> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const;
 
     virtual int getSummaryBlockSize(int desired) const;
 
--- a/data/model/DenseTimeValueModel.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/DenseTimeValueModel.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -37,27 +37,19 @@
 
     if (f1 <= f0) return "";
 
-    float **all = new float *[ch];
-    for (int c = 0; c < ch; ++c) {
-        all[c] = new float[f1 - f0];
-    }
+    auto data = getMultiChannelData(0, ch - 1, f0, f1 - f0);
 
-    sv_frame_t n = getMultiChannelData(0, ch - 1, f0, f1 - f0, all);
-
+    if (data.empty() || data[0].empty()) return "";
+    
     QStringList list;
-    for (sv_frame_t i = 0; i < n; ++i) {
+    for (sv_frame_t i = 0; in_range_for(data[0], i); ++i) {
         QStringList parts;
         parts << QString("%1").arg(f0 + i);
-        for (int c = 0; c < ch; ++c) {
-            parts << QString("%1").arg(all[c][i]);
+        for (int c = 0; in_range_for(data, c); ++c) {
+            parts << QString("%1").arg(data[c][i]);
         }
         list << parts.join(delimiter);
     }
 
-    for (int c = 0; c < ch; ++c) {
-        delete[] all[c];
-    }
-    delete[] all;
-
     return list.join("\n");
 }
--- a/data/model/DenseTimeValueModel.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/DenseTimeValueModel.h	Mon Jun 15 12:19:47 2015 +0100
@@ -57,22 +57,22 @@
 
     /**
      * Get the specified set of samples from the given channel of the
-     * model in single-precision floating-point format.  Return the
-     * number of samples actually retrieved.
+     * model in single-precision floating-point format. Returned
+     * vector may have fewer samples than requested, if the end of
+     * file was reached.
+     *
      * If the channel is given as -1, mix all available channels and
      * return the result.
      */
-    virtual sv_frame_t getData(int channel, sv_frame_t start, sv_frame_t count,
-                               float *buffer) const = 0;
+    virtual std::vector<float> getData(int channel, sv_frame_t start, sv_frame_t count) const = 0;
 
     /**
-     * Get the specified set of samples from given contiguous range
-     * of channels of the model in single-precision floating-point
-     * format.  Return the number of sample frames actually retrieved.
+     * Get the specified set of samples from given contiguous range of
+     * channels of the model in single-precision floating-point
+     * format. Returned vector may have fewer samples than requested,
+     * if the end of file was reached.
      */
-    virtual sv_frame_t getMultiChannelData(int fromchannel, int tochannel,
-                                           sv_frame_t start, sv_frame_t count,
-                                           float **buffers) const = 0;
+    virtual std::vector<std::vector<float>> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const = 0;
 
     virtual bool canPlay() const { return true; }
     virtual QString getDefaultPlayClipId() const { return ""; }
--- a/data/model/FFTModel.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/FFTModel.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -256,22 +256,31 @@
 vector<float>
 FFTModel::getSourceDataUncached(pair<sv_frame_t, sv_frame_t> range) const
 {
-    vector<float> data(range.second - range.first, 0.f);
     decltype(range.first) pfx = 0;
     if (range.first < 0) {
         pfx = -range.first;
         range = { 0, range.second };
     }
-//    cerr << "requesting " << range.second - range.first << " from file" << endl;
-    (void) m_model->getData(m_channel,
-                            range.first,
-                            range.second - range.first,
-                            &data[pfx]);
+
+    auto data = m_model->getData(m_channel,
+                                 range.first,
+                                 range.second - range.first);
+
+    // don't return a partial frame
+    data.resize(range.second - range.first, 0.f);
+
+    if (pfx > 0) {
+        vector<float> pad(pfx, 0.f);
+        data.insert(data.begin(), pad.begin(), pad.end());
+    }
+    
     if (m_channel == -1) {
 	int channels = m_model->getChannelCount();
 	if (channels > 1) {
-	    for (int i = 0; in_range_for(data, i); ++i) {
-		data[i] /= float(channels);
+            int n = int(data.size());
+            float factor = 1.f / float(channels);
+	    for (int i = 0; i < n; ++i) {
+		data[i] *= factor;
 	    }
 	}
     }
--- a/data/model/Model.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/Model.h	Mon Jun 15 12:19:47 2015 +0100
@@ -24,8 +24,6 @@
 #include "base/BaseTypes.h"
 #include "base/DataExportOptions.h"
 
-typedef std::vector<float> SampleBlock;
-
 class ZoomConstraint;
 class AlignmentModel;
 
--- a/data/model/WaveFileModel.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/WaveFileModel.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -32,6 +32,8 @@
 
 #include <cassert>
 
+using namespace std;
+
 //#define DEBUG_WAVE_FILE_MODEL 1
 
 PowerOfSqrtTwoZoomConstraint
@@ -107,7 +109,7 @@
         if (m_reader) {
             int decodeCompletion = m_reader->getDecodeCompletion();
             if (decodeCompletion < 90) *completion = decodeCompletion;
-            else *completion = std::min(*completion, decodeCompletion);
+            else *completion = min(*completion, decodeCompletion);
         }
         if (*completion != 0 &&
             *completion != 100 &&
@@ -184,77 +186,47 @@
     return "";
 }
     
-sv_frame_t
-WaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count,
-                       float *buffer) const
+vector<float>
+WaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const
 {
     // Always read these directly from the file. 
-    // This is used for e.g. audio playback.
-    // Could be much more efficient (although compiler optimisation will help)
+    // This is used for e.g. audio playback or input to transforms.
 
 #ifdef DEBUG_WAVE_FILE_MODEL
     cout << "WaveFileModel::getData[" << this << "]: " << channel << ", " << start << ", " << count << ", " << buffer << endl;
 #endif
 
-    if (start >= m_startFrame) {
-        start -= m_startFrame;
-    } else {
-        for (sv_frame_t i = 0; i < count; ++i) {
-            buffer[i] = 0.f;
-        }
-        if (count <= m_startFrame - start) {
-            return 0;
-        } else {
-            count -= (m_startFrame - start);
-            start = 0;
+    if (!m_reader || !m_reader->isOK() || count == 0) {
+        return {};
+    }
+
+    if (channel != -1) {
+        // get a single channel
+        auto data = getMultiChannelData(channel, channel, start, count);
+        if (data.empty()) return {};
+        else return data[0];
+    }
+
+    // channel == -1, mix down all channels
+
+    auto all = getMultiChannelData(0, getChannelCount()-1, start, count);
+    if (all.empty()) return {};
+
+    sv_frame_t n = all[0].size();
+    vector<float> result(n, 0.f);
+
+    for (int c = 0; in_range_for(all, c); ++c) {
+        for (sv_frame_t i = 0; i < n; ++i) {
+            result[i] += all[c][i];
         }
     }
 
-    if (!m_reader || !m_reader->isOK() || count == 0) {
-        for (sv_frame_t i = 0; i < count; ++i) buffer[i] = 0.f;
-        return 0;
-    }
-
-#ifdef DEBUG_WAVE_FILE_MODEL
-//    SVDEBUG << "WaveFileModel::getValues(" << channel << ", "
-//              << start << ", " << end << "): calling reader" << endl;
-#endif
-
-    int channels = getChannelCount();
-
-    SampleBlock frames = m_reader->getInterleavedFrames(start, count);
-
-    sv_frame_t i = 0;
-
-    int ch0 = channel, ch1 = channel;
-    if (channel == -1) {
-	ch0 = 0;
-	ch1 = channels - 1;
-    }
-    
-    while (i < count) {
-
-	buffer[i] = 0.0;
-
-	for (int ch = ch0; ch <= ch1; ++ch) {
-
-	    sv_frame_t index = i * channels + ch;
-	    if (index >= (sv_frame_t)frames.size()) break;
-            
-	    float sample = frames[index];
-	    buffer[i] += sample;
-	}
-
-	++i;
-    }
-
-    return i;
+    return result;
 }
 
-sv_frame_t
+vector<vector<float>>
 WaveFileModel::getMultiChannelData(int fromchannel, int tochannel,
-                                   sv_frame_t start, sv_frame_t count,
-                                   float **buffer) const
+                                   sv_frame_t start, sv_frame_t count) const
 {
 #ifdef DEBUG_WAVE_FILE_MODEL
     cout << "WaveFileModel::getData[" << this << "]: " << fromchannel << "," << tochannel << ", " << start << ", " << count << ", " << buffer << endl;
@@ -266,73 +238,47 @@
         cerr << "ERROR: WaveFileModel::getData: fromchannel ("
                   << fromchannel << ") > tochannel (" << tochannel << ")"
                   << endl;
-        return 0;
+        return {};
     }
 
     if (tochannel >= channels) {
         cerr << "ERROR: WaveFileModel::getData: tochannel ("
                   << tochannel << ") >= channel count (" << channels << ")"
                   << endl;
-        return 0;
+        return {};
     }
 
-    if (fromchannel == tochannel) {
-        return getData(fromchannel, start, count, buffer[0]);
+    if (!m_reader || !m_reader->isOK() || count == 0) {
+        return {};
     }
 
     int reqchannels = (tochannel - fromchannel) + 1;
 
-    // Always read these directly from the file. 
-    // This is used for e.g. audio playback.
-    // Could be much more efficient (although compiler optimisation will help)
-
     if (start >= m_startFrame) {
         start -= m_startFrame;
     } else {
-        for (int c = 0; c < reqchannels; ++c) {
-            for (sv_frame_t i = 0; i < count; ++i) buffer[c][i] = 0.f;
-        }
         if (count <= m_startFrame - start) {
-            return 0;
+            return {};
         } else {
             count -= (m_startFrame - start);
             start = 0;
         }
     }
 
-    if (!m_reader || !m_reader->isOK() || count == 0) {
-        for (int c = 0; c < reqchannels; ++c) {
-            for (sv_frame_t i = 0; i < count; ++i) buffer[c][i] = 0.f;
+    vector<float> interleaved = m_reader->getInterleavedFrames(start, count);
+    if (channels == 1) return { interleaved };
+
+    sv_frame_t obtained = interleaved.size() / channels;
+    vector<vector<float>> result(reqchannels, vector<float>(obtained, 0.f));
+
+    for (int c = fromchannel; c <= tochannel; ++c) {
+        int destc = c - fromchannel;
+        for (int i = 0; i < obtained; ++i) {
+            result[destc][i] = interleaved[i * channels + c];
         }
-        return 0;
     }
-
-    SampleBlock frames = m_reader->getInterleavedFrames(start, count);
-
-    sv_frame_t i = 0;
-
-    sv_frame_t index = 0, available = frames.size();
-
-    while (i < count) {
-
-        if (index >= available) break;
-
-        int destc = 0;
-
-        for (int c = 0; c < channels; ++c) {
-            
-            if (c >= fromchannel && c <= tochannel) {
-                buffer[destc][i] = frames[index];
-                ++destc;
-            }
-
-            ++index;
-        }
-
-        ++i;
-    }
-
-    return i;
+    
+    return result;
 }
 
 int
@@ -521,16 +467,16 @@
 
     if (blockStart > start) {
         Range startRange = getSummary(channel, start, blockStart - start);
-        range.setMin(std::min(range.min(), startRange.min()));
-        range.setMax(std::max(range.max(), startRange.max()));
-        range.setAbsmean(std::min(range.absmean(), startRange.absmean()));
+        range.setMin(min(range.min(), startRange.min()));
+        range.setMax(max(range.max(), startRange.max()));
+        range.setAbsmean(min(range.absmean(), startRange.absmean()));
     }
 
     if (blockEnd < start + count) {
         Range endRange = getSummary(channel, blockEnd, start + count - blockEnd);
-        range.setMin(std::min(range.min(), endRange.min()));
-        range.setMax(std::max(range.max(), endRange.max()));
-        range.setAbsmean(std::min(range.absmean(), endRange.absmean()));
+        range.setMin(min(range.min(), endRange.min()));
+        range.setMax(max(range.max(), endRange.max()));
+        range.setAbsmean(min(range.absmean(), endRange.absmean()));
     }
 
     return range;
@@ -605,7 +551,7 @@
     
     sv_frame_t frame = 0;
     const sv_frame_t readBlockSize = 16384;
-    SampleBlock block;
+    vector<float> block;
 
     if (!m_model.isOK()) return;
     
--- a/data/model/WaveFileModel.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/WaveFileModel.h	Mon Jun 15 12:19:47 2015 +0100
@@ -62,12 +62,9 @@
 
     void setStartFrame(sv_frame_t startFrame) { m_startFrame = startFrame; }
 
-    virtual sv_frame_t getData(int channel, sv_frame_t start, sv_frame_t count,
-                               float *buffer) const;
+    virtual std::vector<float> getData(int channel, sv_frame_t start, sv_frame_t count) const;
 
-    virtual sv_frame_t getMultiChannelData(int fromchannel, int tochannel,
-                                           sv_frame_t start, sv_frame_t count,
-                                           float **buffers) const;
+    virtual std::vector<std::vector<float>> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const;
 
     virtual int getSummaryBlockSize(int desired) const;
 
@@ -123,7 +120,7 @@
     bool m_exiting;
     static PowerOfSqrtTwoZoomConstraint m_zoomConstraint;
 
-    mutable SampleBlock m_directRead;
+    mutable std::vector<float> m_directRead;
     mutable sv_frame_t m_lastDirectReadStart;
     mutable sv_frame_t m_lastDirectReadCount;
     mutable QMutex m_directReadMutex;
--- a/data/model/WritableWaveFileModel.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/WritableWaveFileModel.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -28,6 +28,8 @@
 #include <iostream>
 #include <stdint.h>
 
+using namespace std;
+
 //#define DEBUG_WRITABLE_WAVE_FILE_MODEL 1
 
 WritableWaveFileModel::WritableWaveFileModel(sv_samplerate_t sampleRate,
@@ -169,21 +171,19 @@
     return m_frameCount;
 }
 
-sv_frame_t
-WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count,
-                               float *buffer) const
+vector<float>
+WritableWaveFileModel::getData(int channel, sv_frame_t start, sv_frame_t count) const
 {
-    if (!m_model || m_model->getChannelCount() == 0) return 0;
-    return m_model->getData(channel, start, count, buffer);
+    if (!m_model || m_model->getChannelCount() == 0) return {};
+    return m_model->getData(channel, start, count);
 }
 
-sv_frame_t
+vector<vector<float>>
 WritableWaveFileModel::getMultiChannelData(int fromchannel, int tochannel,
-                                           sv_frame_t start, sv_frame_t count,
-                                           float **buffers) const
+                                           sv_frame_t start, sv_frame_t count) const
 {
-    if (!m_model || m_model->getChannelCount() == 0) return 0;
-    return m_model->getMultiChannelData(fromchannel, tochannel, start, count, buffers);
+    if (!m_model || m_model->getChannelCount() == 0) return {};
+    return m_model->getMultiChannelData(fromchannel, tochannel, start, count);
 }    
 
 int
--- a/data/model/WritableWaveFileModel.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/WritableWaveFileModel.h	Mon Jun 15 12:19:47 2015 +0100
@@ -60,12 +60,9 @@
 
     void setStartFrame(sv_frame_t startFrame);
 
-    virtual sv_frame_t getData(int channel, sv_frame_t start, sv_frame_t count,
-                               float *buffer) const;
+    virtual std::vector<float> getData(int channel, sv_frame_t start, sv_frame_t count) const;
 
-    virtual sv_frame_t getMultiChannelData(int fromchannel, int tochannel,
-                                           sv_frame_t start, sv_frame_t count,
-                                           float **buffer) const;
+    virtual std::vector<std::vector<float>> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const;
 
     virtual int getSummaryBlockSize(int desired) const;
 
--- a/data/model/test/MockWaveModel.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/test/MockWaveModel.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -24,40 +24,39 @@
     }
 }
 
-sv_frame_t
-MockWaveModel::getData(int channel, sv_frame_t start, sv_frame_t count,
-		       float *buffer) const
+vector<float>
+MockWaveModel::getData(int channel, sv_frame_t start, sv_frame_t count) const
 {
     sv_frame_t i = 0;
 
 //    cerr << "MockWaveModel::getData(" << channel << "," << start << "," << count << "): ";
 
+    vector<float> data;
+    
     while (i < count) {
 	sv_frame_t idx = start + i;
 	if (!in_range_for(m_data[channel], idx)) break;
-	buffer[i] = m_data[channel][idx];
-//	cerr << buffer[i] << " ";
+	data.push_back(m_data[channel][idx]);
+//	cerr << data[i] << " ";
 	++i;
     }
 
 //    cerr << endl;
     
-    return i;
+    return data;
 }
 
-sv_frame_t
+vector<vector<float>>
 MockWaveModel::getMultiChannelData(int fromchannel, int tochannel,
-				   sv_frame_t start, sv_frame_t count,
-				   float **buffers) const
+				   sv_frame_t start, sv_frame_t count) const
 {
-    sv_frame_t min = count;
-
+    vector<vector<float>> data(tochannel - fromchannel + 1);
+    
     for (int c = fromchannel; c <= tochannel; ++c) {
-	sv_frame_t n = getData(c, start, count, buffers[c]);
-	if (n < min) min = n;
+        data.push_back(getData(c, start, count));
     }
 
-    return min;
+    return data;
 }
 
 vector<float>
--- a/data/model/test/MockWaveModel.h	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/test/MockWaveModel.h	Mon Jun 15 12:19:47 2015 +0100
@@ -41,11 +41,8 @@
     virtual float getValueMaximum() const { return  1.f; }
     virtual int getChannelCount() const { return int(m_data.size()); }
     
-    virtual sv_frame_t getData(int channel, sv_frame_t start, sv_frame_t count,
-                               float *buffer) const;
-    virtual sv_frame_t getMultiChannelData(int fromchannel, int tochannel,
-					   sv_frame_t start, sv_frame_t count,
-					   float **buffers) const;
+    virtual std::vector<float> getData(int channel, sv_frame_t start, sv_frame_t count) const;
+    virtual std::vector<std::vector<float>> getMultiChannelData(int fromchannel, int tochannel, sv_frame_t start, sv_frame_t count) const;
 
     virtual bool canPlay() const { return true; }
     virtual QString getDefaultPlayClipId() const { return ""; }
--- a/data/model/test/test.pro	Sat Jun 13 08:47:05 2015 +0100
+++ b/data/model/test/test.pro	Mon Jun 15 12:19:47 2015 +0100
@@ -61,7 +61,7 @@
 
 !win32 {
     !macx* {
-        QMAKE_POST_LINK=./$${TARGET}
+        QMAKE_POST_LINK=valgrind ./$${TARGET}
     }
     macx* {
         QMAKE_POST_LINK=./$${TARGET}.app/Contents/MacOS/$${TARGET}
--- a/transform/FeatureExtractionModelTransformer.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/transform/FeatureExtractionModelTransformer.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -787,31 +787,28 @@
 
     if (channelCount == 1) {
 
-        got = input->getData(m_input.getChannel(), startFrame, size,
-                             buffers[0] + offset);
+        auto data = input->getData(m_input.getChannel(), startFrame, size);
+        got = data.size();
+
+        copy(data.begin(), data.end(), buffers[0] + offset);
 
         if (m_input.getChannel() == -1 && input->getChannelCount() > 1) {
             // use mean instead of sum, as plugin input
             float cc = float(input->getChannelCount());
-            for (sv_frame_t i = 0; i < size; ++i) {
+            for (sv_frame_t i = 0; i < got; ++i) {
                 buffers[0][i + offset] /= cc;
             }
         }
 
     } else {
 
-        float **writebuf = buffers;
-        if (offset > 0) {
-            writebuf = new float *[channelCount];
-            for (int i = 0; i < channelCount; ++i) {
-                writebuf[i] = buffers[i] + offset;
+        auto data = input->getMultiChannelData(0, channelCount-1, startFrame, size);
+        if (!data.empty()) {
+            got = data[0].size();
+            for (int c = 0; in_range_for(data, c); ++c) {
+                copy(data[c].begin(), data[c].end(), buffers[c] + offset);
             }
         }
-
-        got = input->getMultiChannelData
-            (0, channelCount-1, startFrame, size, writebuf);
-
-        if (writebuf != buffers) delete[] writebuf;
     }
 
     while (got < size) {
--- a/transform/RealTimeEffectModelTransformer.cpp	Sat Jun 13 08:47:05 2015 +0100
+++ b/transform/RealTimeEffectModelTransformer.cpp	Mon Jun 15 12:19:47 2015 +0100
@@ -191,10 +191,14 @@
 
 	if (channelCount == 1) {
             if (inbufs && inbufs[0]) {
-                got = input->getData
-                    (m_input.getChannel(), blockFrame, blockSize, inbufs[0]);
+                auto data = input->getData
+                    (m_input.getChannel(), blockFrame, blockSize);
+                got = data.size();
+                for (sv_frame_t i = 0; i < got; ++i) {
+                    inbufs[0][i] = data[i];
+                }
                 while (got < blockSize) {
-                    inbufs[0][got++] = 0.0;
+                    inbufs[0][got++] = 0.f;
                 }          
                 for (int ch = 1; ch < (int)m_plugin->getAudioInputCount(); ++ch) {
                     for (sv_frame_t i = 0; i < blockSize; ++i) {
@@ -204,9 +208,14 @@
             }
 	} else {
             if (inbufs && inbufs[0]) {
-                got = input->getMultiChannelData(0, channelCount - 1,
-                                                 blockFrame, blockSize,
-                                                 inbufs);
+                auto data = input->getMultiChannelData
+                    (0, channelCount - 1, blockFrame, blockSize);
+                if (!data.empty()) got = data[0].size();
+                for (int ch = 0; ch < channelCount; ++ch) {
+                    for (sv_frame_t i = 0; i < got; ++i) {
+                        inbufs[ch][i] = data[ch][i];
+                    }
+                }
                 while (got < blockSize) {
                     for (int ch = 0; ch < channelCount; ++ch) {
                         inbufs[ch][got] = 0.0;