changeset 363:0e30c8ec15a0

* Add wave file model method for reading more than one channel at once, avoiding ludicrously expensive backward seeks and double-reads when playing multi-channel files or using them as inputs to feature extraction plugins
author Chris Cannam
date Thu, 24 Jan 2008 14:35:43 +0000
parents cc4eb32efc6c
children 876a79afd376
files data/model/AggregateWaveModel.cpp data/model/AggregateWaveModel.h data/model/DenseTimeValueModel.h data/model/WaveFileModel.cpp data/model/WaveFileModel.h data/model/WritableWaveFileModel.cpp data/model/WritableWaveFileModel.h plugin/transform/FeatureExtractionModelTransformer.cpp plugin/transform/FeatureExtractionModelTransformer.h
diffstat 9 files changed, 183 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/data/model/AggregateWaveModel.cpp	Thu Jan 24 11:03:59 2008 +0000
+++ b/data/model/AggregateWaveModel.cpp	Thu Jan 24 14:35:43 2008 +0000
@@ -175,6 +175,21 @@
     if (mixing) delete[] readbuf;
     return sz;
 }
+
+size_t
+AggregateWaveModel::getData(size_t fromchannel, size_t tochannel,
+                            size_t start, size_t count,
+                            float **buffer) const
+{
+    size_t min = count;
+
+    for (size_t c = fromchannel; c <= tochannel; ++c) {
+        size_t here = getData(c, start, count, buffer[c - fromchannel]);
+        if (here < min) min = here;
+    }
+    
+    return min;
+}
         
 void
 AggregateWaveModel::getSummaries(size_t channel, size_t start, size_t count,
--- a/data/model/AggregateWaveModel.h	Thu Jan 24 11:03:59 2008 +0000
+++ b/data/model/AggregateWaveModel.h	Thu Jan 24 14:35:43 2008 +0000
@@ -67,6 +67,10 @@
     virtual size_t getData(int channel, size_t start, size_t count,
                            double *buffer) const;
 
+    virtual size_t getData(size_t fromchannel, size_t tochannel,
+                           size_t start, size_t count,
+                           float **buffer) const;
+
     virtual void getSummaries(size_t channel, size_t start, size_t count,
                               RangeBlock &ranges,
                               size_t &blockSize) const;
--- a/data/model/DenseTimeValueModel.h	Thu Jan 24 11:03:59 2008 +0000
+++ b/data/model/DenseTimeValueModel.h	Thu Jan 24 14:35:43 2008 +0000
@@ -71,6 +71,15 @@
     virtual size_t getData(int channel, size_t start, size_t count,
                            double *buffer) 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.
+     */
+    virtual size_t getData(size_t fromchannel, size_t tochannel,
+                           size_t start, size_t count,
+                           float **buffers) const = 0;
+
     QString getTypeName() const { return tr("Dense Time-Value"); }
 };
 
--- a/data/model/WaveFileModel.cpp	Thu Jan 24 11:03:59 2008 +0000
+++ b/data/model/WaveFileModel.cpp	Thu Jan 24 14:35:43 2008 +0000
@@ -189,7 +189,7 @@
     // This is used for e.g. audio playback.
     // Could be much more efficient (although compiler optimisation will help)
 
-    if (start > m_startFrame) {
+    if (start >= m_startFrame) {
         start -= m_startFrame;
     } else {
         for (size_t i = 0; i < count; ++i) buffer[i] = 0.f;
@@ -211,12 +211,14 @@
 //              << start << ", " << end << "): calling reader" << std::endl;
 #endif
 
-    SampleBlock frames;
+    int channels = getChannelCount();
+
+    SampleBlock frames(count * channels);
     m_reader->getInterleavedFrames(start, count, frames);
 
     size_t i = 0;
 
-    int ch0 = channel, ch1 = channel, channels = getChannelCount();
+    int ch0 = channel, ch1 = channel;
     if (channel == -1) {
 	ch0 = 0;
 	ch1 = channels - 1;
@@ -262,12 +264,14 @@
         return 0;
     }
 
-    SampleBlock frames;
+    int channels = getChannelCount();
+
+    SampleBlock frames(count * channels);
     m_reader->getInterleavedFrames(start, count, frames);
 
     size_t i = 0;
 
-    int ch0 = channel, ch1 = channel, channels = getChannelCount();
+    int ch0 = channel, ch1 = channel;
     if (channel == -1) {
 	ch0 = 0;
 	ch1 = channels - 1;
@@ -292,6 +296,89 @@
     return i;
 }
 
+size_t
+WaveFileModel::getData(size_t fromchannel, size_t tochannel,
+                       size_t start, size_t count,
+                       float **buffer) const
+{
+    size_t channels = getChannelCount();
+
+    if (fromchannel > tochannel) {
+        std::cerr << "ERROR: WaveFileModel::getData: fromchannel ("
+                  << fromchannel << ") > tochannel (" << tochannel << ")"
+                  << std::endl;
+        return 0;
+    }
+
+    if (tochannel >= channels) {
+        std::cerr << "ERROR: WaveFileModel::getData: tochannel ("
+                  << tochannel << ") >= channel count (" << channels << ")"
+                  << std::endl;
+        return 0;
+    }
+
+    if (fromchannel == tochannel) {
+        return getData(fromchannel, start, count, buffer[0]);
+    }
+
+    size_t 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 (size_t c = 0; c < reqchannels; ++c) {
+            for (size_t i = 0; i < count; ++i) buffer[c][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) {
+        for (size_t c = 0; c < reqchannels; ++c) {
+            for (size_t i = 0; i < count; ++i) buffer[c][i] = 0.f;
+        }
+        return 0;
+    }
+
+    SampleBlock frames(count * channels);
+    m_reader->getInterleavedFrames(start, count, frames);
+
+    size_t i = 0;
+
+    int ch0 = fromchannel, ch1 = tochannel;
+    
+    size_t index = 0, available = frames.size();
+
+    while (i < count) {
+
+        if (index >= available) break;
+
+        size_t destc = 0;
+
+        for (size_t c = 0; c < channels; ++c) {
+            
+            if (c >= fromchannel && c <= tochannel) {
+                buffer[destc][i] = frames[index];
+                ++destc;
+            }
+
+            ++index;
+        }
+
+        ++i;
+    }
+
+    return i;
+}
+
 void
 WaveFileModel::getSummaries(size_t channel, size_t start, size_t count,
                             RangeBlock &ranges, size_t &blockSize) const
--- a/data/model/WaveFileModel.h	Thu Jan 24 11:03:59 2008 +0000
+++ b/data/model/WaveFileModel.h	Thu Jan 24 14:35:43 2008 +0000
@@ -68,6 +68,10 @@
     virtual size_t getData(int channel, size_t start, size_t count,
                            double *buffer) const;
 
+    virtual size_t getData(size_t fromchannel, size_t tochannel,
+                           size_t start, size_t count,
+                           float **buffers) const;
+
     virtual void getSummaries(size_t channel, size_t start, size_t count,
                               RangeBlock &ranges,
                               size_t &blockSize) const;
--- a/data/model/WritableWaveFileModel.cpp	Thu Jan 24 11:03:59 2008 +0000
+++ b/data/model/WritableWaveFileModel.cpp	Thu Jan 24 14:35:43 2008 +0000
@@ -185,11 +185,18 @@
                                double *buffer) const
 {
     if (!m_model || m_model->getChannelCount() == 0) return 0;
-//    std::cerr << "WritableWaveFileModel::getValues(" << channel << ", "
-//              << start << ", " << end << "): calling model" << std::endl;
     return m_model->getData(channel, start, count, buffer);
 }
 
+size_t
+WritableWaveFileModel::getData(size_t fromchannel, size_t tochannel,
+                               size_t start, size_t count,
+                               float **buffers) const
+{
+    if (!m_model || m_model->getChannelCount() == 0) return 0;
+    return m_model->getData(fromchannel, tochannel, start, count, buffers);
+}    
+
 void
 WritableWaveFileModel::getSummaries(size_t channel, size_t start, size_t count,
                                     RangeBlock &ranges,
--- a/data/model/WritableWaveFileModel.h	Thu Jan 24 11:03:59 2008 +0000
+++ b/data/model/WritableWaveFileModel.h	Thu Jan 24 14:35:43 2008 +0000
@@ -68,6 +68,10 @@
     virtual size_t getData(int channel, size_t start, size_t count,
                            double *buffer) const;
 
+    virtual size_t getData(size_t fromchannel, size_t tochannel,
+                           size_t start, size_t count,
+                           float **buffer) const;
+
     virtual void getSummaries(size_t channel, size_t start, size_t count,
                               RangeBlock &ranges, size_t &blockSize) const;
 
--- a/plugin/transform/FeatureExtractionModelTransformer.cpp	Thu Jan 24 11:03:59 2008 +0000
+++ b/plugin/transform/FeatureExtractionModelTransformer.cpp	Thu Jan 24 14:35:43 2008 +0000
@@ -385,17 +385,16 @@
 
 	// channelCount is either m_input.getModel()->channelCount or 1
 
-        for (size_t ch = 0; ch < channelCount; ++ch) {
-            if (frequencyDomain) {
+        if (frequencyDomain) {
+            for (size_t ch = 0; ch < channelCount; ++ch) {
                 int column = (blockFrame - startFrame) / stepSize;
                 for (size_t i = 0; i <= blockSize/2; ++i) {
                     fftModels[ch]->getValuesAt
                         (column, i, buffers[ch][i*2], buffers[ch][i*2+1]);
                 }
-            } else {
-                getFrames(ch, channelCount, 
-                          blockFrame, blockSize, buffers[ch]);
-            }                
+            }
+        } else {
+            getFrames(channelCount, blockFrame, blockSize, buffers);
         }
 
 	Vamp::Plugin::FeatureSet features = m_plugin->process
@@ -435,15 +434,17 @@
 }
 
 void
-FeatureExtractionModelTransformer::getFrames(int channel, int channelCount,
-                                            long startFrame, long size,
-                                            float *buffer)
+FeatureExtractionModelTransformer::getFrames(int channelCount,
+                                             long startFrame, long size,
+                                             float **buffers)
 {
     long offset = 0;
 
     if (startFrame < 0) {
-        for (int i = 0; i < size && startFrame + i < 0; ++i) {
-            buffer[i] = 0.0f;
+        for (int c = 0; c < channelCount; ++c) {
+            for (int i = 0; i < size && startFrame + i < 0; ++i) {
+                buffers[c][i] = 0.0f;
+            }
         }
         offset = -startFrame;
         size -= offset;
@@ -453,24 +454,43 @@
 
     DenseTimeValueModel *input = getConformingInput();
     if (!input) return;
+    
+    long got = 0;
 
-    long got = input->getData
-        ((channelCount == 1 ? m_input.getChannel() : channel),
-         startFrame, size, buffer + offset);
+    if (channelCount == 1) {
+
+        got = input->getData(m_input.getChannel(), startFrame, size,
+                             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 (long i = 0; i < size; ++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;
+            }
+        }
+
+        got = input->getData(0, channelCount-1, startFrame, size, writebuf);
+
+        if (writebuf != buffers) delete[] writebuf;
+    }
 
     while (got < size) {
-        buffer[offset + got] = 0.0;
+        for (int c = 0; c < channelCount; ++c) {
+            buffers[c][got + offset] = 0.0;
+        }
         ++got;
     }
-
-    if (m_input.getChannel() == -1 && channelCount == 1 &&
-        input->getChannelCount() > 1) {
-        // use mean instead of sum, as plugin input
-        int cc = input->getChannelCount();
-        for (long i = 0; i < size; ++i) {
-            buffer[i] /= cc;
-        }
-    }
 }
 
 void
--- a/plugin/transform/FeatureExtractionModelTransformer.h	Thu Jan 24 11:03:59 2008 +0000
+++ b/plugin/transform/FeatureExtractionModelTransformer.h	Thu Jan 24 14:35:43 2008 +0000
@@ -47,8 +47,8 @@
 
     void setCompletion(int);
 
-    void getFrames(int channel, int channelCount,
-                   long startFrame, long size, float *buffer);
+    void getFrames(int channelCount, long startFrame, long size,
+                   float **buffer);
 
     // just casts
     DenseTimeValueModel *getConformingInput();