Mercurial > hg > sv-dependency-builds
diff src/rubberband-1.8.1/ladspa/RubberBandPitchShifter.cpp @ 10:37bf6b4a2645
Add FFTW3
author | Chris Cannam |
---|---|
date | Wed, 20 Mar 2013 15:35:50 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rubberband-1.8.1/ladspa/RubberBandPitchShifter.cpp Wed Mar 20 15:35:50 2013 +0000 @@ -0,0 +1,563 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Rubber Band Library + An audio time-stretching and pitch-shifting library. + Copyright 2007-2012 Particular Programs Ltd. + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of the + License, or (at your option) any later version. See the file + COPYING included with this distribution for more information. + + Alternatively, if you have a valid commercial licence for the + Rubber Band Library obtained by agreement with the copyright + holders, you may redistribute and/or modify it under the terms + described in that licence. + + If you wish to distribute code using the Rubber Band Library + under terms other than those of the GNU General Public License, + you must obtain a valid commercial licence before doing so. +*/ + +#include "RubberBandPitchShifter.h" + +#include "RubberBandStretcher.h" + +#include <iostream> +#include <cmath> + +using namespace RubberBand; + +using std::cout; +using std::cerr; +using std::endl; +using std::min; + +const char *const +RubberBandPitchShifter::portNamesMono[PortCountMono] = +{ + "latency", + "Cents", + "Semitones", + "Octaves", + "Crispness", + "Formant Preserving", + "Faster", + "Input", + "Output" +}; + +const char *const +RubberBandPitchShifter::portNamesStereo[PortCountStereo] = +{ + "latency", + "Cents", + "Semitones", + "Octaves", + "Crispness", + "Formant Preserving", + "Faster", + "Input L", + "Output L", + "Input R", + "Output R" +}; + +const LADSPA_PortDescriptor +RubberBandPitchShifter::portsMono[PortCountMono] = +{ + LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, + LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO +}; + +const LADSPA_PortDescriptor +RubberBandPitchShifter::portsStereo[PortCountStereo] = +{ + LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, + LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, + LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, + LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, + LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO +}; + +const LADSPA_PortRangeHint +RubberBandPitchShifter::hintsMono[PortCountMono] = +{ + { 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 | // semitones + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_INTEGER, + -12.0, 12.0 }, + { LADSPA_HINT_DEFAULT_0 | // octaves + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_INTEGER, + -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 | // formant preserving + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, + { LADSPA_HINT_DEFAULT_0 | // fast + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, + { 0, 0, 0 }, + { 0, 0, 0 } +}; + +const LADSPA_PortRangeHint +RubberBandPitchShifter::hintsStereo[PortCountStereo] = +{ + { 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 | // semitones + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_INTEGER, + -12.0, 12.0 }, + { LADSPA_HINT_DEFAULT_0 | // octaves + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_INTEGER, + -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 | // formant preserving + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, + { LADSPA_HINT_DEFAULT_0 | // fast + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE | + LADSPA_HINT_TOGGLED, + 0.0, 1.0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } +}; + +const LADSPA_Properties +RubberBandPitchShifter::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE; + +const LADSPA_Descriptor +RubberBandPitchShifter::ladspaDescriptorMono = +{ + 2979, // "Unique" ID + "rubberband-pitchshifter-mono", // Label + properties, + "Rubber Band Mono Pitch Shifter", // Name + "Breakfast Quay", + "GPL", + PortCountMono, + portsMono, + portNamesMono, + hintsMono, + 0, // Implementation data + instantiate, + connectPort, + activate, + run, + 0, // Run adding + 0, // Set run adding gain + deactivate, + cleanup +}; + +const LADSPA_Descriptor +RubberBandPitchShifter::ladspaDescriptorStereo = +{ + 9792, // "Unique" ID + "rubberband-pitchshifter-stereo", // Label + properties, + "Rubber Band Stereo Pitch Shifter", // Name + "Breakfast Quay", + "GPL", + PortCountStereo, + portsStereo, + portNamesStereo, + hintsStereo, + 0, // Implementation data + instantiate, + connectPort, + activate, + run, + 0, // Run adding + 0, // Set run adding gain + deactivate, + cleanup +}; + +const LADSPA_Descriptor * +RubberBandPitchShifter::getDescriptor(unsigned long index) +{ + if (index == 0) return &ladspaDescriptorMono; + if (index == 1) return &ladspaDescriptorStereo; + else return 0; +} + +RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels) : + m_latency(0), + m_cents(0), + m_semitones(0), + m_octaves(0), + m_crispness(0), + m_formant(0), + m_fast(0), + m_ratio(1.0), + m_prevRatio(1.0), + m_currentCrispness(-1), + m_currentFormant(false), + m_currentFast(false), + m_blockSize(1024), + m_reserve(1024), + m_minfill(0), + m_stretcher(new RubberBandStretcher + (sampleRate, channels, + 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; + + 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() +{ + delete m_stretcher; + for (size_t c = 0; c < m_channels; ++c) { + delete m_outputBuffer[c]; + delete[] m_scratch[c]; + } +} + +LADSPA_Handle +RubberBandPitchShifter::instantiate(const LADSPA_Descriptor *desc, unsigned long rate) +{ + if (desc->PortCount == ladspaDescriptorMono.PortCount) { + return new RubberBandPitchShifter(rate, 1); + } else if (desc->PortCount == ladspaDescriptorStereo.PortCount) { + return new RubberBandPitchShifter(rate, 2); + } + return 0; +} + +void +RubberBandPitchShifter::connectPort(LADSPA_Handle handle, + unsigned long port, LADSPA_Data *location) +{ + RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle; + + float **ports[PortCountStereo] = { + &shifter->m_latency, + &shifter->m_cents, + &shifter->m_semitones, + &shifter->m_octaves, + &shifter->m_crispness, + &shifter->m_formant, + &shifter->m_fast, + &shifter->m_input[0], + &shifter->m_output[0], + &shifter->m_input[1], + &shifter->m_output[1] + }; + + if (shifter->m_channels == 1) { + if (port >= PortCountMono) return; + } else { + if (port >= PortCountStereo) return; + } + + *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->activateImpl(); +} + +void +RubberBandPitchShifter::activateImpl() +{ + updateRatio(); + m_prevRatio = m_ratio; + m_stretcher->reset(); + m_stretcher->setPitchScale(m_ratio); + + for (size_t c = 0; c < m_channels; ++c) { + m_outputBuffer[c]->reset(); + m_outputBuffer[c]->zero(m_reserve); + } + + m_minfill = 0; + + // prime stretcher +// for (int i = 0; i < 8; ++i) { +// 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 +RubberBandPitchShifter::run(LADSPA_Handle handle, unsigned long samples) +{ + RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle; + shifter->runImpl(samples); +} + +void +RubberBandPitchShifter::updateRatio() +{ + 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); +} + +void +RubberBandPitchShifter::updateCrispness() +{ + if (!m_crispness) return; + + int c = lrintf(*m_crispness); + if (c == m_currentCrispness) return; + if (c < 0 || c > 3) return; + RubberBandStretcher *s = m_stretcher; + + switch (c) { + case 0: + s->setPhaseOption(RubberBandStretcher::OptionPhaseIndependent); + s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth); + break; + case 1: + s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); + s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth); + break; + case 2: + s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); + s->setTransientsOption(RubberBandStretcher::OptionTransientsMixed); + break; + case 3: + s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); + s->setTransientsOption(RubberBandStretcher::OptionTransientsCrisp); + break; + } + + m_currentCrispness = c; +} + +void +RubberBandPitchShifter::updateFormant() +{ + if (!m_formant) return; + + bool f = (*m_formant > 0.5f); + if (f == m_currentFormant) return; + + RubberBandStretcher *s = m_stretcher; + + s->setFormantOption(f ? + RubberBandStretcher::OptionFormantPreserved : + RubberBandStretcher::OptionFormantShifted); + + m_currentFormant = f; +} + +void +RubberBandPitchShifter::updateFast() +{ + if (!m_fast) return; + + bool f = (*m_fast > 0.5f); + if (f == m_currentFast) return; + + RubberBandStretcher *s = m_stretcher; + + s->setPitchOption(f ? + RubberBandStretcher::OptionPitchHighSpeed : + RubberBandStretcher::OptionPitchHighConsistency); + + m_currentFast = f; +} + +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 + offset > insamples) block = insamples - offset; + + runImpl(block, offset); + + offset += block; + } +} + +void +RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset) +{ +// cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << endl; + +// static int incount = 0, outcount = 0; + + updateRatio(); + if (m_ratio != m_prevRatio) { + m_stretcher->setPitchScale(m_ratio); + m_prevRatio = m_ratio; + } + + if (m_latency) { + *m_latency = float(m_stretcher->getLatency() + m_reserve); +// cerr << "latency = " << *m_latency << endl; + } + + updateCrispness(); + updateFormant(); + updateFast(); + + const int samples = insamples; + int processed = 0; + size_t outTotal = 0; + + float *ptrs[2]; + + int rs = m_outputBuffer[0]->getReadSpace(); + if (rs < int(m_minfill)) { +// cerr << "temporary expansion (have " << rs << ", want " << m_reserve << ")" << endl; + m_stretcher->setTimeRatio(1.1); // fill up temporarily + } else if (rs > 8192) { +// cerr << "temporary reduction (have " << rs << ", want " << m_reserve << ")" << endl; + m_stretcher->setTimeRatio(0.9); // reduce temporarily + } else { + m_stretcher->setTimeRatio(1.0); + } + + while (processed < samples) { + + // 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(); + int inchunk = min(samples - processed, toCauseProcessing); + for (size_t c = 0; c < m_channels; ++c) { + ptrs[c] = &(m_input[c][offset + processed]); + } + m_stretcher->process(ptrs, inchunk, false); + processed += inchunk; + + int avail = m_stretcher->available(); + int writable = m_outputBuffer[0]->getWriteSpace(); + int outchunk = min(avail, writable); + size_t actual = m_stretcher->retrieve(m_scratch, outchunk); + outTotal += actual; + +// incount += inchunk; +// outcount += actual; + +// cout << "avail: " << avail << ", outchunk = " << outchunk; +// if (actual != outchunk) cout << " (" << actual << ")"; +// cout << endl; + + outchunk = actual; + + for (size_t c = 0; c < m_channels; ++c) { + if (int(m_outputBuffer[c]->getWriteSpace()) < outchunk) { + cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << endl; + } + m_outputBuffer[c]->write(m_scratch[c], outchunk); + } + } + + for (size_t c = 0; c < m_channels; ++c) { + int toRead = m_outputBuffer[c]->getReadSpace(); + if (toRead < samples && c == 0) { + cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << toRead << endl; + } + int chunk = min(toRead, samples); + m_outputBuffer[c]->read(&(m_output[c][offset]), chunk); + } + + if (m_minfill == 0) { + m_minfill = m_outputBuffer[0]->getReadSpace(); +// cerr << "minfill = " << m_minfill << endl; + } +} + +void +RubberBandPitchShifter::deactivate(LADSPA_Handle handle) +{ + activate(handle); // both functions just reset the plugin +} + +void +RubberBandPitchShifter::cleanup(LADSPA_Handle handle) +{ + delete (RubberBandPitchShifter *)handle; +} +