# HG changeset patch # User Chris Cannam # Date 1201185343 0 # Node ID 0e30c8ec15a05f1a3d21cfbfdb2ebd3339079bc3 # Parent cc4eb32efc6c415e5a26929899436218229063ba * 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 diff -r cc4eb32efc6c -r 0e30c8ec15a0 data/model/AggregateWaveModel.cpp --- 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, diff -r cc4eb32efc6c -r 0e30c8ec15a0 data/model/AggregateWaveModel.h --- 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; diff -r cc4eb32efc6c -r 0e30c8ec15a0 data/model/DenseTimeValueModel.h --- 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"); } }; diff -r cc4eb32efc6c -r 0e30c8ec15a0 data/model/WaveFileModel.cpp --- 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 diff -r cc4eb32efc6c -r 0e30c8ec15a0 data/model/WaveFileModel.h --- 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; diff -r cc4eb32efc6c -r 0e30c8ec15a0 data/model/WritableWaveFileModel.cpp --- 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, diff -r cc4eb32efc6c -r 0e30c8ec15a0 data/model/WritableWaveFileModel.h --- 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; diff -r cc4eb32efc6c -r 0e30c8ec15a0 plugin/transform/FeatureExtractionModelTransformer.cpp --- 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 diff -r cc4eb32efc6c -r 0e30c8ec15a0 plugin/transform/FeatureExtractionModelTransformer.h --- 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();