diff --git a/Makefile.in b/Makefile.in index 89bf9d3..5aac345 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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) diff --git a/src/Resampler.cpp b/src/Resampler.cpp index 69cde65..5bcff08 100644 --- a/src/Resampler.cpp +++ b/src/Resampler.cpp @@ -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; } diff --git a/src/StretchCalculator.cpp b/src/StretchCalculator.cpp index f212a87..e5fc019 100644 --- a/src/StretchCalculator.cpp +++ b/src/StretchCalculator.cpp @@ -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; diff --git a/src/StretcherImpl.cpp b/src/StretcherImpl.cpp index c069a5f..992f070 100644 --- a/src/StretcherImpl.cpp +++ b/src/StretcherImpl.cpp @@ -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 diff --git a/src/StretcherProcess.cpp b/src/StretcherProcess.cpp index 9ff6bee..50ef553 100644 --- a/src/StretcherProcess.cpp +++ b/src/StretcherProcess.cpp @@ -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); diff --git a/src/ladspa/RubberBandPitchShifter.cpp b/src/ladspa/RubberBandPitchShifter.cpp index d28b086..0788eb0 100644 --- a/src/ladspa/RubberBandPitchShifter.cpp +++ b/src/ladspa/RubberBandPitchShifter.cpp @@ -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(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(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); } } diff --git a/src/ladspa/RubberBandPitchShifter.h b/src/ladspa/RubberBandPitchShifter.h index e490573..0b32370 100644 --- a/src/ladspa/RubberBandPitchShifter.h +++ b/src/ladspa/RubberBandPitchShifter.h @@ -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 *m_outputBuffer[2]; diff --git a/src/ladspa/ladspa-plugin.map b/src/ladspa/ladspa-plugin.map new file mode 100644 index 0000000..6498bd4 --- /dev/null +++ b/src/ladspa/ladspa-plugin.map @@ -0,0 +1,4 @@ +{ + global: ladspa_descriptor; + local: *; +}; diff --git a/src/vamp/vamp-plugin.map b/src/vamp/vamp-plugin.map new file mode 100644 index 0000000..8292465 --- /dev/null +++ b/src/vamp/vamp-plugin.map @@ -0,0 +1,4 @@ +{ + global: vampGetPluginDescriptor; + local: *; +};