* Fix under/overruns in ladspa plugin

This commit is contained in:
Chris Cannam
2008-07-04 14:19:32 +00:00
parent 59a160eb3f
commit 86462dbd9f
9 changed files with 145 additions and 71 deletions

View File

@@ -1,5 +1,7 @@
CXX := @CXX@
#CXXFLAGS := -DHAVE_FFTW3 -DFFTW_DOUBLE_ONLY -DNO_THREAD_CHECKS @CXXFLAGS@ @SRC_CFLAGS@ @SNDFILE_CFLAGS@ @FFTW_CFLAGS@ @Vamp_CFLAGS@ -Irubberband -Isrc $(OPTFLAGS)
OPTFLAGS := -O3 -ffast-math -ftree-vectorize -ftree-vect-loop-version -march=pentium4 -msse -msse2
CXXFLAGS := -DHAVE_FFTW3 -DFFTW_DOUBLE_ONLY -DNO_THREAD_CHECKS @CXXFLAGS@ @SRC_CFLAGS@ @SNDFILE_CFLAGS@ @FFTW_CFLAGS@ @Vamp_CFLAGS@ -Irubberband -Isrc $(OPTFLAGS)
CFLAGS := @CFLAGS@ $(OPTFLAGS)
LDFLAGS := @LDFLAGS@ -lpthread $(LDFLAGS)

View File

@@ -65,6 +65,7 @@ protected:
SRC_STATE *m_src;
float *m_iin;
float *m_iout;
float m_lastRatio;
int m_channels;
int m_iinsize;
int m_ioutsize;
@@ -76,6 +77,7 @@ D_SRC::D_SRC(Resampler::Quality quality, int channels, int maxBufferSize,
m_src(0),
m_iin(0),
m_iout(0),
m_lastRatio(1.f),
m_channels(channels),
m_iinsize(0),
m_ioutsize(0),
@@ -105,6 +107,8 @@ D_SRC::D_SRC(Resampler::Quality quality, int channels, int maxBufferSize,
m_iin = (float *)malloc(m_iinsize * sizeof(float));
m_iout = (float *)malloc(m_ioutsize * sizeof(float));
}
reset();
}
D_SRC::~D_SRC()
@@ -171,6 +175,8 @@ D_SRC::resample(const float *const R__ *const R__ in,
}
}
m_lastRatio = ratio;
return data.output_frames_gen;
}

View File

@@ -195,6 +195,9 @@ StretchCalculator::calculateSingle(double ratio,
m_prevDf = df;
bool ratioChanged = (ratio != m_prevRatio);
m_prevRatio = ratio;
if (isTransient && m_transientAmnesty == 0) {
if (m_debugLevel > 1) {
std::cerr << "StretchCalculator::calculateSingle: transient"
@@ -210,9 +213,8 @@ StretchCalculator::calculateSingle(double ratio,
return -int(increment);
}
if (m_prevRatio != ratio) {
if (ratioChanged) {
m_recovery = m_divergence / ((m_sampleRate / 10.0) / increment);
m_prevRatio = ratio;
}
if (m_transientAmnesty > 0) --m_transientAmnesty;

View File

@@ -236,9 +236,27 @@ RubberBandStretcher::Impl::setPitchScale(double fs)
}
if (fs == m_pitchScale) return;
bool was1 = (m_pitchScale == 1.f);
bool rbs = resampleBeforeStretching();
m_pitchScale = fs;
reconfigure();
if (!(m_options & OptionPitchHighConsistency) &&
(was1 || resampleBeforeStretching() != rbs) &&
m_pitchScale != 1.f) {
cerr << "reset resampler" << endl;
// resampling mode has changed
for (int c = 0; c < m_channels; ++c) {
if (m_channelData[c]->resampler) {
m_channelData[c]->resampler->reset();
}
}
}
}
double

View File

@@ -1042,7 +1042,6 @@ RubberBandStretcher::Impl::writeChunk(size_t channel, size_t shiftIncrement, boo
1.0 / m_pitchScale,
last);
writeOutput(*cd.outbuf, cd.resamplebuf,
outframes, cd.outCount, theoreticalOut);

View File

@@ -80,27 +80,27 @@ RubberBandPitchShifter::portsStereo[PortCountStereo] =
const LADSPA_PortRangeHint
RubberBandPitchShifter::hintsMono[PortCountMono] =
{
{ 0, 0, 0 },
{ LADSPA_HINT_DEFAULT_0 |
{ 0, 0, 0 }, // latency
{ LADSPA_HINT_DEFAULT_0 | // cents
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE,
-100.0, 100.0 },
{ LADSPA_HINT_DEFAULT_0 |
{ LADSPA_HINT_DEFAULT_0 | // semitones
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-12.0, 12.0 },
{ LADSPA_HINT_DEFAULT_0 |
{ LADSPA_HINT_DEFAULT_0 | // octaves
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-4.0, 4.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM |
-3.0, 3.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
0.0, 3.0 },
{ LADSPA_HINT_DEFAULT_0 |
{ LADSPA_HINT_DEFAULT_0 | // formant preserving
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
@@ -112,27 +112,27 @@ RubberBandPitchShifter::hintsMono[PortCountMono] =
const LADSPA_PortRangeHint
RubberBandPitchShifter::hintsStereo[PortCountStereo] =
{
{ 0, 0, 0 },
{ LADSPA_HINT_DEFAULT_0 |
{ 0, 0, 0 }, // latency
{ LADSPA_HINT_DEFAULT_0 | // cents
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE,
-100.0, 100.0 },
{ LADSPA_HINT_DEFAULT_0 |
{ LADSPA_HINT_DEFAULT_0 | // semitones
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-12.0, 12.0 },
{ LADSPA_HINT_DEFAULT_0 |
{ LADSPA_HINT_DEFAULT_0 | // octaves
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-4.0, 4.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM |
-3.0, 3.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
0.0, 3.0 },
{ LADSPA_HINT_DEFAULT_0 |
{ LADSPA_HINT_DEFAULT_0 | // formant preserving
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
@@ -213,32 +213,29 @@ RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels)
m_prevRatio(1.0),
m_currentCrispness(-1),
m_currentFormant(false),
m_extraLatency(128),
m_blockSize(1024),
m_reserve(1024),
m_stretcher(new RubberBandStretcher
(sampleRate, channels,
RubberBandStretcher::OptionProcessRealTime)),
RubberBandStretcher::OptionProcessRealTime |
RubberBandStretcher::OptionPitchHighConsistency)),
m_sampleRate(sampleRate),
m_channels(channels)
{
for (size_t c = 0; c < m_channels; ++c) {
m_input[c] = 0;
m_output[c] = 0;
//!!! size must be at least max process size plus m_extraLatency:
m_outputBuffer[c] = new RingBuffer<float>(8092); //!!!
m_outputBuffer[c]->zero(m_extraLatency);
//!!! size must be at least max process size:
m_scratch[c] = new float[16384];//!!!
for (int i = 0; i < 16384; ++i) {
m_scratch[c][i] = 0.f;
}
}
int reqd = m_stretcher->getSamplesRequired();
m_stretcher->process(m_scratch, reqd, false);
int avail = m_stretcher->available();
std::cerr << "construction: reqd = " << reqd << ", available = " << avail << std::endl;
if (avail > 0) {
m_stretcher->retrieve(m_scratch, avail);
int bufsize = m_blockSize + m_reserve + 8192;
m_outputBuffer[c] = new RingBuffer<float>(bufsize);
m_scratch[c] = new float[bufsize];
for (int i = 0; i < bufsize; ++i) m_scratch[c][i] = 0.f;
}
activateImpl();
}
RubberBandPitchShifter::~RubberBandPitchShifter()
@@ -281,16 +278,40 @@ RubberBandPitchShifter::connectPort(LADSPA_Handle handle,
};
*ports[port] = (float *)location;
if (shifter->m_latency) {
*(shifter->m_latency) =
float(shifter->m_stretcher->getLatency() + shifter->m_reserve);
}
}
void
RubberBandPitchShifter::activate(LADSPA_Handle handle)
{
RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle;
shifter->updateRatio();
shifter->m_prevRatio = shifter->m_ratio;
shifter->m_stretcher->reset();
shifter->m_stretcher->setPitchScale(shifter->m_ratio);
shifter->activateImpl();
}
void
RubberBandPitchShifter::activateImpl()
{
updateRatio();
m_prevRatio = m_ratio;
m_stretcher->reset();
m_stretcher->setPitchScale(m_ratio);
for (int c = 0; c < m_channels; ++c) {
m_outputBuffer[c]->reset();
m_outputBuffer[c]->zero(m_reserve);
}
// prime stretcher
int reqd = m_stretcher->getSamplesRequired();
m_stretcher->process(m_scratch, reqd, false);
int avail = m_stretcher->available();
if (avail > 0) {
m_stretcher->retrieve(m_scratch, avail);
}
}
void
@@ -303,9 +324,9 @@ RubberBandPitchShifter::run(LADSPA_Handle handle, unsigned long samples)
void
RubberBandPitchShifter::updateRatio()
{
double oct = *m_octaves;
oct += *m_semitones / 12;
oct += *m_cents / 1200;
double oct = (m_octaves ? *m_octaves : 0.0);
oct += (m_semitones ? *m_semitones : 0.0) / 12;
oct += (m_cents ? *m_cents : 0.0) / 1200;
m_ratio = pow(2.0, oct);
}
@@ -360,10 +381,30 @@ RubberBandPitchShifter::updateFormant()
void
RubberBandPitchShifter::runImpl(unsigned long insamples)
{
unsigned long offset = 0;
// We have to break up the input into chunks like this because
// insamples could be arbitrarily large and our output buffer is
// of limited size
while (offset < insamples) {
unsigned long block = (unsigned long)m_blockSize;
if (block > insamples) block = insamples;
runImpl(block, offset);
offset += block;
}
}
void
RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
{
// std::cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << std::endl;
static int incount = 0, outcount = 0;
// static int incount = 0, outcount = 0;
updateRatio();
if (m_ratio != m_prevRatio) {
@@ -372,43 +413,51 @@ RubberBandPitchShifter::runImpl(unsigned long insamples)
}
if (m_latency) {
*m_latency = float(m_stretcher->getLatency() + m_extraLatency);
*m_latency = float(m_stretcher->getLatency() + m_reserve);
// std::cerr << "latency = " << *m_latency << std::endl;
}
updateCrispness();
updateFormant();
int samples = insamples;
const int samples = insamples;
int processed = 0;
size_t outTotal = 0;
float *ptrs[2];
// We have to break up the input into chunks like this because
// insamples could be arbitrarily large
if (m_outputBuffer[0]->getReadSpace() < m_reserve) {
m_stretcher->setTimeRatio(1.1); // fill up temporarily
} else if (m_outputBuffer[0]->getReadSpace() > 8192) {
m_stretcher->setTimeRatio(0.9); // reduce temporarily
} else {
m_stretcher->setTimeRatio(1.0);
}
while (processed < samples) {
//!!! size_t:
// never feed more than the minimum necessary number of
// samples at a time; ensures nothing will overflow internally
// and we don't need to call setMaxProcessSize
int toCauseProcessing = m_stretcher->getSamplesRequired();
// std::cout << "to-cause: " << toCauseProcessing << ", remain = " << samples - processed;
int inchunk = std::min(samples - processed, toCauseProcessing);
for (size_t c = 0; c < m_channels; ++c) {
ptrs[c] = &(m_input[c][processed]);
ptrs[c] = &(m_input[c][offset + processed]);
}
m_stretcher->process(ptrs, inchunk, false);
processed += inchunk;
incount += inchunk; //!!!
int avail = m_stretcher->available();
int writable = m_outputBuffer[0]->getWriteSpace();
int outchunk = std::min(avail, writable);
size_t actual = m_stretcher->retrieve(m_scratch, outchunk);
outTotal += actual;
outcount += actual;
// std::cout << ", avail: " << avail << ", outchunk = " << outchunk;
// incount += inchunk;
// outcount += actual;
// std::cout << "avail: " << avail << ", outchunk = " << outchunk;
// if (actual != outchunk) std::cout << " (" << actual << ")";
// std::cout << std::endl;
@@ -421,27 +470,14 @@ RubberBandPitchShifter::runImpl(unsigned long insamples)
m_outputBuffer[c]->write(m_scratch[c], outchunk);
}
}
std::cout << "in: " << incount << ", out: " << outcount << ", loss: " << incount - outcount << std::endl;
// std::cout << "processed = " << processed << " in, " << outTotal << " out" << ", fill = " << m_outputBuffer[0]->getReadSpace() << " of " << m_outputBuffer[0]->getSize() << std::endl;
for (size_t c = 0; c < m_channels; ++c) {
int avail = m_outputBuffer[c]->getReadSpace();
// std::cout << "avail: " << avail << std::endl;
if (avail < samples && c == 0) {
std::cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << avail << std::endl;
int toRead = m_outputBuffer[c]->getReadSpace();
if (toRead < samples && c == 0) {
std::cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << toRead << std::endl;
}
int chunk = std::min(avail, samples);
// std::cout << "out chunk: " << chunk << std::endl;
m_outputBuffer[c]->read(m_output[c], chunk);
}
static int minr = -1;
int avail = m_outputBuffer[0]->getReadSpace();
if (minr == -1 || (avail >= 0 && avail < minr)) {
std::cerr << "RubberBandPitchShifter::runImpl: new min remaining " << avail << " from " << minr << std::endl;
minr = avail;
int chunk = std::min(toRead, samples);
m_outputBuffer[c]->read(&(m_output[c][offset]), chunk);
}
}

View File

@@ -67,7 +67,9 @@ protected:
static void deactivate(LADSPA_Handle);
static void cleanup(LADSPA_Handle);
void activateImpl();
void runImpl(unsigned long);
void runImpl(unsigned long, unsigned long offset);
void updateRatio();
void updateCrispness();
void updateFormant();
@@ -85,7 +87,8 @@ protected:
int m_currentCrispness;
bool m_currentFormant;
size_t m_extraLatency;
size_t m_blockSize;
size_t m_reserve;
RubberBand::RubberBandStretcher *m_stretcher;
RubberBand::RingBuffer<float> *m_outputBuffer[2];

View File

@@ -0,0 +1,4 @@
{
global: ladspa_descriptor;
local: *;
};

4
src/vamp/vamp-plugin.map Normal file
View File

@@ -0,0 +1,4 @@
{
global: vampGetPluginDescriptor;
local: *;
};