Chris@10: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@10: Chris@10: /* Chris@10: Rubber Band Library Chris@10: An audio time-stretching and pitch-shifting library. Chris@10: Copyright 2007-2012 Particular Programs Ltd. Chris@10: Chris@10: This program is free software; you can redistribute it and/or Chris@10: modify it under the terms of the GNU General Public License as Chris@10: published by the Free Software Foundation; either version 2 of the Chris@10: License, or (at your option) any later version. See the file Chris@10: COPYING included with this distribution for more information. Chris@10: Chris@10: Alternatively, if you have a valid commercial licence for the Chris@10: Rubber Band Library obtained by agreement with the copyright Chris@10: holders, you may redistribute and/or modify it under the terms Chris@10: described in that licence. Chris@10: Chris@10: If you wish to distribute code using the Rubber Band Library Chris@10: under terms other than those of the GNU General Public License, Chris@10: you must obtain a valid commercial licence before doing so. Chris@10: */ Chris@10: Chris@10: #include "RubberBandPitchShifter.h" Chris@10: Chris@10: #include "RubberBandStretcher.h" Chris@10: Chris@10: #include Chris@10: #include Chris@10: Chris@10: using namespace RubberBand; Chris@10: Chris@10: using std::cout; Chris@10: using std::cerr; Chris@10: using std::endl; Chris@10: using std::min; Chris@10: Chris@10: const char *const Chris@10: RubberBandPitchShifter::portNamesMono[PortCountMono] = Chris@10: { Chris@10: "latency", Chris@10: "Cents", Chris@10: "Semitones", Chris@10: "Octaves", Chris@10: "Crispness", Chris@10: "Formant Preserving", Chris@10: "Faster", Chris@10: "Input", Chris@10: "Output" Chris@10: }; Chris@10: Chris@10: const char *const Chris@10: RubberBandPitchShifter::portNamesStereo[PortCountStereo] = Chris@10: { Chris@10: "latency", Chris@10: "Cents", Chris@10: "Semitones", Chris@10: "Octaves", Chris@10: "Crispness", Chris@10: "Formant Preserving", Chris@10: "Faster", Chris@10: "Input L", Chris@10: "Output L", Chris@10: "Input R", Chris@10: "Output R" Chris@10: }; Chris@10: Chris@10: const LADSPA_PortDescriptor Chris@10: RubberBandPitchShifter::portsMono[PortCountMono] = Chris@10: { Chris@10: LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, Chris@10: LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO Chris@10: }; Chris@10: Chris@10: const LADSPA_PortDescriptor Chris@10: RubberBandPitchShifter::portsStereo[PortCountStereo] = Chris@10: { Chris@10: LADSPA_PORT_OUTPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, Chris@10: LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, Chris@10: LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, Chris@10: LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO Chris@10: }; Chris@10: Chris@10: const LADSPA_PortRangeHint Chris@10: RubberBandPitchShifter::hintsMono[PortCountMono] = Chris@10: { Chris@10: { 0, 0, 0 }, // latency Chris@10: { LADSPA_HINT_DEFAULT_0 | // cents Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE, Chris@10: -100.0, 100.0 }, Chris@10: { LADSPA_HINT_DEFAULT_0 | // semitones Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_INTEGER, Chris@10: -12.0, 12.0 }, Chris@10: { LADSPA_HINT_DEFAULT_0 | // octaves Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_INTEGER, Chris@10: -3.0, 3.0 }, Chris@10: { LADSPA_HINT_DEFAULT_MAXIMUM | // crispness Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_INTEGER, Chris@10: 0.0, 3.0 }, Chris@10: { LADSPA_HINT_DEFAULT_0 | // formant preserving Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_TOGGLED, Chris@10: 0.0, 1.0 }, Chris@10: { LADSPA_HINT_DEFAULT_0 | // fast Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_TOGGLED, Chris@10: 0.0, 1.0 }, Chris@10: { 0, 0, 0 }, Chris@10: { 0, 0, 0 } Chris@10: }; Chris@10: Chris@10: const LADSPA_PortRangeHint Chris@10: RubberBandPitchShifter::hintsStereo[PortCountStereo] = Chris@10: { Chris@10: { 0, 0, 0 }, // latency Chris@10: { LADSPA_HINT_DEFAULT_0 | // cents Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE, Chris@10: -100.0, 100.0 }, Chris@10: { LADSPA_HINT_DEFAULT_0 | // semitones Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_INTEGER, Chris@10: -12.0, 12.0 }, Chris@10: { LADSPA_HINT_DEFAULT_0 | // octaves Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_INTEGER, Chris@10: -3.0, 3.0 }, Chris@10: { LADSPA_HINT_DEFAULT_MAXIMUM | // crispness Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_INTEGER, Chris@10: 0.0, 3.0 }, Chris@10: { LADSPA_HINT_DEFAULT_0 | // formant preserving Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_TOGGLED, Chris@10: 0.0, 1.0 }, Chris@10: { LADSPA_HINT_DEFAULT_0 | // fast Chris@10: LADSPA_HINT_BOUNDED_BELOW | Chris@10: LADSPA_HINT_BOUNDED_ABOVE | Chris@10: LADSPA_HINT_TOGGLED, Chris@10: 0.0, 1.0 }, Chris@10: { 0, 0, 0 }, Chris@10: { 0, 0, 0 }, Chris@10: { 0, 0, 0 }, Chris@10: { 0, 0, 0 } Chris@10: }; Chris@10: Chris@10: const LADSPA_Properties Chris@10: RubberBandPitchShifter::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE; Chris@10: Chris@10: const LADSPA_Descriptor Chris@10: RubberBandPitchShifter::ladspaDescriptorMono = Chris@10: { Chris@10: 2979, // "Unique" ID Chris@10: "rubberband-pitchshifter-mono", // Label Chris@10: properties, Chris@10: "Rubber Band Mono Pitch Shifter", // Name Chris@10: "Breakfast Quay", Chris@10: "GPL", Chris@10: PortCountMono, Chris@10: portsMono, Chris@10: portNamesMono, Chris@10: hintsMono, Chris@10: 0, // Implementation data Chris@10: instantiate, Chris@10: connectPort, Chris@10: activate, Chris@10: run, Chris@10: 0, // Run adding Chris@10: 0, // Set run adding gain Chris@10: deactivate, Chris@10: cleanup Chris@10: }; Chris@10: Chris@10: const LADSPA_Descriptor Chris@10: RubberBandPitchShifter::ladspaDescriptorStereo = Chris@10: { Chris@10: 9792, // "Unique" ID Chris@10: "rubberband-pitchshifter-stereo", // Label Chris@10: properties, Chris@10: "Rubber Band Stereo Pitch Shifter", // Name Chris@10: "Breakfast Quay", Chris@10: "GPL", Chris@10: PortCountStereo, Chris@10: portsStereo, Chris@10: portNamesStereo, Chris@10: hintsStereo, Chris@10: 0, // Implementation data Chris@10: instantiate, Chris@10: connectPort, Chris@10: activate, Chris@10: run, Chris@10: 0, // Run adding Chris@10: 0, // Set run adding gain Chris@10: deactivate, Chris@10: cleanup Chris@10: }; Chris@10: Chris@10: const LADSPA_Descriptor * Chris@10: RubberBandPitchShifter::getDescriptor(unsigned long index) Chris@10: { Chris@10: if (index == 0) return &ladspaDescriptorMono; Chris@10: if (index == 1) return &ladspaDescriptorStereo; Chris@10: else return 0; Chris@10: } Chris@10: Chris@10: RubberBandPitchShifter::RubberBandPitchShifter(int sampleRate, size_t channels) : Chris@10: m_latency(0), Chris@10: m_cents(0), Chris@10: m_semitones(0), Chris@10: m_octaves(0), Chris@10: m_crispness(0), Chris@10: m_formant(0), Chris@10: m_fast(0), Chris@10: m_ratio(1.0), Chris@10: m_prevRatio(1.0), Chris@10: m_currentCrispness(-1), Chris@10: m_currentFormant(false), Chris@10: m_currentFast(false), Chris@10: m_blockSize(1024), Chris@10: m_reserve(1024), Chris@10: m_minfill(0), Chris@10: m_stretcher(new RubberBandStretcher Chris@10: (sampleRate, channels, Chris@10: RubberBandStretcher::OptionProcessRealTime | Chris@10: RubberBandStretcher::OptionPitchHighConsistency)), Chris@10: m_sampleRate(sampleRate), Chris@10: m_channels(channels) Chris@10: { Chris@10: for (size_t c = 0; c < m_channels; ++c) { Chris@10: Chris@10: m_input[c] = 0; Chris@10: m_output[c] = 0; Chris@10: Chris@10: int bufsize = m_blockSize + m_reserve + 8192; Chris@10: Chris@10: m_outputBuffer[c] = new RingBuffer(bufsize); Chris@10: Chris@10: m_scratch[c] = new float[bufsize]; Chris@10: for (int i = 0; i < bufsize; ++i) m_scratch[c][i] = 0.f; Chris@10: } Chris@10: Chris@10: activateImpl(); Chris@10: } Chris@10: Chris@10: RubberBandPitchShifter::~RubberBandPitchShifter() Chris@10: { Chris@10: delete m_stretcher; Chris@10: for (size_t c = 0; c < m_channels; ++c) { Chris@10: delete m_outputBuffer[c]; Chris@10: delete[] m_scratch[c]; Chris@10: } Chris@10: } Chris@10: Chris@10: LADSPA_Handle Chris@10: RubberBandPitchShifter::instantiate(const LADSPA_Descriptor *desc, unsigned long rate) Chris@10: { Chris@10: if (desc->PortCount == ladspaDescriptorMono.PortCount) { Chris@10: return new RubberBandPitchShifter(rate, 1); Chris@10: } else if (desc->PortCount == ladspaDescriptorStereo.PortCount) { Chris@10: return new RubberBandPitchShifter(rate, 2); Chris@10: } Chris@10: return 0; Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::connectPort(LADSPA_Handle handle, Chris@10: unsigned long port, LADSPA_Data *location) Chris@10: { Chris@10: RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle; Chris@10: Chris@10: float **ports[PortCountStereo] = { Chris@10: &shifter->m_latency, Chris@10: &shifter->m_cents, Chris@10: &shifter->m_semitones, Chris@10: &shifter->m_octaves, Chris@10: &shifter->m_crispness, Chris@10: &shifter->m_formant, Chris@10: &shifter->m_fast, Chris@10: &shifter->m_input[0], Chris@10: &shifter->m_output[0], Chris@10: &shifter->m_input[1], Chris@10: &shifter->m_output[1] Chris@10: }; Chris@10: Chris@10: if (shifter->m_channels == 1) { Chris@10: if (port >= PortCountMono) return; Chris@10: } else { Chris@10: if (port >= PortCountStereo) return; Chris@10: } Chris@10: Chris@10: *ports[port] = (float *)location; Chris@10: Chris@10: if (shifter->m_latency) { Chris@10: *(shifter->m_latency) = Chris@10: float(shifter->m_stretcher->getLatency() + shifter->m_reserve); Chris@10: } Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::activate(LADSPA_Handle handle) Chris@10: { Chris@10: RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle; Chris@10: shifter->activateImpl(); Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::activateImpl() Chris@10: { Chris@10: updateRatio(); Chris@10: m_prevRatio = m_ratio; Chris@10: m_stretcher->reset(); Chris@10: m_stretcher->setPitchScale(m_ratio); Chris@10: Chris@10: for (size_t c = 0; c < m_channels; ++c) { Chris@10: m_outputBuffer[c]->reset(); Chris@10: m_outputBuffer[c]->zero(m_reserve); Chris@10: } Chris@10: Chris@10: m_minfill = 0; Chris@10: Chris@10: // prime stretcher Chris@10: // for (int i = 0; i < 8; ++i) { Chris@10: // int reqd = m_stretcher->getSamplesRequired(); Chris@10: // m_stretcher->process(m_scratch, reqd, false); Chris@10: // int avail = m_stretcher->available(); Chris@10: // if (avail > 0) { Chris@10: // m_stretcher->retrieve(m_scratch, avail); Chris@10: // } Chris@10: // } Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::run(LADSPA_Handle handle, unsigned long samples) Chris@10: { Chris@10: RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle; Chris@10: shifter->runImpl(samples); Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::updateRatio() Chris@10: { Chris@10: double oct = (m_octaves ? *m_octaves : 0.0); Chris@10: oct += (m_semitones ? *m_semitones : 0.0) / 12; Chris@10: oct += (m_cents ? *m_cents : 0.0) / 1200; Chris@10: m_ratio = pow(2.0, oct); Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::updateCrispness() Chris@10: { Chris@10: if (!m_crispness) return; Chris@10: Chris@10: int c = lrintf(*m_crispness); Chris@10: if (c == m_currentCrispness) return; Chris@10: if (c < 0 || c > 3) return; Chris@10: RubberBandStretcher *s = m_stretcher; Chris@10: Chris@10: switch (c) { Chris@10: case 0: Chris@10: s->setPhaseOption(RubberBandStretcher::OptionPhaseIndependent); Chris@10: s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth); Chris@10: break; Chris@10: case 1: Chris@10: s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); Chris@10: s->setTransientsOption(RubberBandStretcher::OptionTransientsSmooth); Chris@10: break; Chris@10: case 2: Chris@10: s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); Chris@10: s->setTransientsOption(RubberBandStretcher::OptionTransientsMixed); Chris@10: break; Chris@10: case 3: Chris@10: s->setPhaseOption(RubberBandStretcher::OptionPhaseLaminar); Chris@10: s->setTransientsOption(RubberBandStretcher::OptionTransientsCrisp); Chris@10: break; Chris@10: } Chris@10: Chris@10: m_currentCrispness = c; Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::updateFormant() Chris@10: { Chris@10: if (!m_formant) return; Chris@10: Chris@10: bool f = (*m_formant > 0.5f); Chris@10: if (f == m_currentFormant) return; Chris@10: Chris@10: RubberBandStretcher *s = m_stretcher; Chris@10: Chris@10: s->setFormantOption(f ? Chris@10: RubberBandStretcher::OptionFormantPreserved : Chris@10: RubberBandStretcher::OptionFormantShifted); Chris@10: Chris@10: m_currentFormant = f; Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::updateFast() Chris@10: { Chris@10: if (!m_fast) return; Chris@10: Chris@10: bool f = (*m_fast > 0.5f); Chris@10: if (f == m_currentFast) return; Chris@10: Chris@10: RubberBandStretcher *s = m_stretcher; Chris@10: Chris@10: s->setPitchOption(f ? Chris@10: RubberBandStretcher::OptionPitchHighSpeed : Chris@10: RubberBandStretcher::OptionPitchHighConsistency); Chris@10: Chris@10: m_currentFast = f; Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::runImpl(unsigned long insamples) Chris@10: { Chris@10: unsigned long offset = 0; Chris@10: Chris@10: // We have to break up the input into chunks like this because Chris@10: // insamples could be arbitrarily large and our output buffer is Chris@10: // of limited size Chris@10: Chris@10: while (offset < insamples) { Chris@10: Chris@10: unsigned long block = (unsigned long)m_blockSize; Chris@10: if (block + offset > insamples) block = insamples - offset; Chris@10: Chris@10: runImpl(block, offset); Chris@10: Chris@10: offset += block; Chris@10: } Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset) Chris@10: { Chris@10: // cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << endl; Chris@10: Chris@10: // static int incount = 0, outcount = 0; Chris@10: Chris@10: updateRatio(); Chris@10: if (m_ratio != m_prevRatio) { Chris@10: m_stretcher->setPitchScale(m_ratio); Chris@10: m_prevRatio = m_ratio; Chris@10: } Chris@10: Chris@10: if (m_latency) { Chris@10: *m_latency = float(m_stretcher->getLatency() + m_reserve); Chris@10: // cerr << "latency = " << *m_latency << endl; Chris@10: } Chris@10: Chris@10: updateCrispness(); Chris@10: updateFormant(); Chris@10: updateFast(); Chris@10: Chris@10: const int samples = insamples; Chris@10: int processed = 0; Chris@10: size_t outTotal = 0; Chris@10: Chris@10: float *ptrs[2]; Chris@10: Chris@10: int rs = m_outputBuffer[0]->getReadSpace(); Chris@10: if (rs < int(m_minfill)) { Chris@10: // cerr << "temporary expansion (have " << rs << ", want " << m_reserve << ")" << endl; Chris@10: m_stretcher->setTimeRatio(1.1); // fill up temporarily Chris@10: } else if (rs > 8192) { Chris@10: // cerr << "temporary reduction (have " << rs << ", want " << m_reserve << ")" << endl; Chris@10: m_stretcher->setTimeRatio(0.9); // reduce temporarily Chris@10: } else { Chris@10: m_stretcher->setTimeRatio(1.0); Chris@10: } Chris@10: Chris@10: while (processed < samples) { Chris@10: Chris@10: // never feed more than the minimum necessary number of Chris@10: // samples at a time; ensures nothing will overflow internally Chris@10: // and we don't need to call setMaxProcessSize Chris@10: Chris@10: int toCauseProcessing = m_stretcher->getSamplesRequired(); Chris@10: int inchunk = min(samples - processed, toCauseProcessing); Chris@10: for (size_t c = 0; c < m_channels; ++c) { Chris@10: ptrs[c] = &(m_input[c][offset + processed]); Chris@10: } Chris@10: m_stretcher->process(ptrs, inchunk, false); Chris@10: processed += inchunk; Chris@10: Chris@10: int avail = m_stretcher->available(); Chris@10: int writable = m_outputBuffer[0]->getWriteSpace(); Chris@10: int outchunk = min(avail, writable); Chris@10: size_t actual = m_stretcher->retrieve(m_scratch, outchunk); Chris@10: outTotal += actual; Chris@10: Chris@10: // incount += inchunk; Chris@10: // outcount += actual; Chris@10: Chris@10: // cout << "avail: " << avail << ", outchunk = " << outchunk; Chris@10: // if (actual != outchunk) cout << " (" << actual << ")"; Chris@10: // cout << endl; Chris@10: Chris@10: outchunk = actual; Chris@10: Chris@10: for (size_t c = 0; c < m_channels; ++c) { Chris@10: if (int(m_outputBuffer[c]->getWriteSpace()) < outchunk) { Chris@10: cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << endl; Chris@10: } Chris@10: m_outputBuffer[c]->write(m_scratch[c], outchunk); Chris@10: } Chris@10: } Chris@10: Chris@10: for (size_t c = 0; c < m_channels; ++c) { Chris@10: int toRead = m_outputBuffer[c]->getReadSpace(); Chris@10: if (toRead < samples && c == 0) { Chris@10: cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << toRead << endl; Chris@10: } Chris@10: int chunk = min(toRead, samples); Chris@10: m_outputBuffer[c]->read(&(m_output[c][offset]), chunk); Chris@10: } Chris@10: Chris@10: if (m_minfill == 0) { Chris@10: m_minfill = m_outputBuffer[0]->getReadSpace(); Chris@10: // cerr << "minfill = " << m_minfill << endl; Chris@10: } Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::deactivate(LADSPA_Handle handle) Chris@10: { Chris@10: activate(handle); // both functions just reset the plugin Chris@10: } Chris@10: Chris@10: void Chris@10: RubberBandPitchShifter::cleanup(LADSPA_Handle handle) Chris@10: { Chris@10: delete (RubberBandPitchShifter *)handle; Chris@10: } Chris@10: