From 90ad1274d8c29bc8549ffeb20dca83d0efac352f Mon Sep 17 00:00:00 2001 From: Chris Cannam Date: Mon, 13 Jun 2022 11:49:04 +0100 Subject: [PATCH] Add R3 support to plugins --- ladspa-lv2/RubberBandR3PitchShifter.cpp | 646 +++++++++++++++++++ ladspa-lv2/RubberBandR3PitchShifter.h | 149 +++++ ladspa-lv2/ladspa-rubberband.cat | 2 + ladspa-lv2/libmain-ladspa.cpp | 7 +- ladspa-lv2/libmain-lv2.cpp | 7 +- ladspa-lv2/rubberband.lv2/lv2-rubberband.ttl | 110 ++++ ladspa-lv2/rubberband.lv2/manifest.ttl | 10 + src/finer/R3StretcherImpl.cpp | 1 + 8 files changed, 930 insertions(+), 2 deletions(-) create mode 100644 ladspa-lv2/RubberBandR3PitchShifter.cpp create mode 100644 ladspa-lv2/RubberBandR3PitchShifter.h diff --git a/ladspa-lv2/RubberBandR3PitchShifter.cpp b/ladspa-lv2/RubberBandR3PitchShifter.cpp new file mode 100644 index 0000000..ee7c4a1 --- /dev/null +++ b/ladspa-lv2/RubberBandR3PitchShifter.cpp @@ -0,0 +1,646 @@ +/* -*- 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-2022 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 "RubberBandR3PitchShifter.h" + +#include "RubberBandStretcher.h" + +#include +#include + +using namespace RubberBand; + +using std::cout; +using std::cerr; +using std::endl; +using std::min; + +#ifdef RB_PLUGIN_LADSPA + +const char *const +RubberBandR3PitchShifter::portNamesMono[PortCountMono] = +{ + "latency", + "Cents", + "Semitones", + "Octaves", + "Formant Preserving", + "Wet-Dry Mix", + "Input", + "Output" +}; + +const char *const +RubberBandR3PitchShifter::portNamesStereo[PortCountStereo] = +{ + "latency", + "Cents", + "Semitones", + "Octaves", + "Formant Preserving", + "Wet-Dry Mix", + "Input L", + "Output L", + "Input R", + "Output R" +}; + +const LADSPA_PortDescriptor +RubberBandR3PitchShifter::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_AUDIO, + LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO +}; + +const LADSPA_PortDescriptor +RubberBandR3PitchShifter::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_AUDIO, + LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO, + LADSPA_PORT_INPUT | LADSPA_PORT_AUDIO, + LADSPA_PORT_OUTPUT | LADSPA_PORT_AUDIO +}; + +const LADSPA_PortRangeHint +RubberBandR3PitchShifter::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, + -2.0, 2.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 | // wet-dry mix + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE, + 0.0, 1.0 }, + { 0, 0, 0 }, + { 0, 0, 0 } +}; + +const LADSPA_PortRangeHint +RubberBandR3PitchShifter::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, + -2.0, 2.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 | // wet-dry mix + LADSPA_HINT_BOUNDED_BELOW | + LADSPA_HINT_BOUNDED_ABOVE, + 0.0, 1.0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 } +}; + +const LADSPA_Properties +RubberBandR3PitchShifter::properties = LADSPA_PROPERTY_HARD_RT_CAPABLE; + +const LADSPA_Descriptor +RubberBandR3PitchShifter::ladspaDescriptorMono = +{ + 29790, // "Unique" ID + "rubberband-r3-pitchshifter-mono", // Label + properties, + "Rubber Band R3 Mono Pitch Shifter", // Name + "Breakfast Quay", + "GPL", + PortCountMono, + portsMono, + portNamesMono, + hintsMono, + nullptr, // Implementation data + instantiate, + connectPort, + activate, + run, + nullptr, // Run adding + nullptr, // Set run adding gain + deactivate, + cleanup +}; + +const LADSPA_Descriptor +RubberBandR3PitchShifter::ladspaDescriptorStereo = +{ + 97920, // "Unique" ID + "rubberband-r3-pitchshifter-stereo", // Label + properties, + "Rubber Band R3 Stereo Pitch Shifter", // Name + "Breakfast Quay", + "GPL", + PortCountStereo, + portsStereo, + portNamesStereo, + hintsStereo, + nullptr, // Implementation data + instantiate, + connectPort, + activate, + run, + nullptr, // Run adding + nullptr, // Set run adding gain + deactivate, + cleanup +}; + +const LADSPA_Descriptor * +RubberBandR3PitchShifter::getDescriptor(unsigned long index) +{ + if (index == 0) return &ladspaDescriptorMono; + if (index == 1) return &ladspaDescriptorStereo; + else return 0; +} + +#else + +const LV2_Descriptor +RubberBandR3PitchShifter::lv2DescriptorMono = +{ + "http://breakfastquay.com/rdf/lv2-rubberband-r3#mono", + instantiate, + connectPort, + activate, + run, + deactivate, + cleanup, + nullptr +}; + +const LV2_Descriptor +RubberBandR3PitchShifter::lv2DescriptorStereo = +{ + "http://breakfastquay.com/rdf/lv2-rubberband-r3#stereo", + instantiate, + connectPort, + activate, + run, + deactivate, + cleanup, + nullptr +}; + +const LV2_Descriptor * +RubberBandR3PitchShifter::getDescriptor(uint32_t index) +{ + if (index == 0) return &lv2DescriptorMono; + if (index == 1) return &lv2DescriptorStereo; + else return 0; +} + +#endif + +RubberBandR3PitchShifter::RubberBandR3PitchShifter(int sampleRate, size_t channels) : + m_latency(nullptr), + m_cents(nullptr), + m_semitones(nullptr), + m_octaves(nullptr), + m_formant(nullptr), + m_wetDry(nullptr), + m_ratio(1.0), + m_prevRatio(1.0), + m_currentFormant(false), + m_blockSize(1024), + m_reserve(8192), + m_bufsize(0), + m_minfill(0), + m_stretcher(new RubberBandStretcher + (sampleRate, channels, + RubberBandStretcher::OptionProcessRealTime | + RubberBandStretcher::OptionEngineFiner)), + m_sampleRate(sampleRate), + m_channels(channels) +{ + m_input = new float *[m_channels]; + m_output = new float *[m_channels]; + + m_outputBuffer = new RingBuffer *[m_channels]; + m_delayMixBuffer = new RingBuffer *[m_channels]; + m_scratch = new float *[m_channels]; + m_inptrs = new float *[m_channels]; + + m_bufsize = m_blockSize + m_reserve + 8192; + + for (size_t c = 0; c < m_channels; ++c) { + + m_input[c] = 0; + m_output[c] = 0; + + m_outputBuffer[c] = new RingBuffer(m_bufsize); + m_delayMixBuffer[c] = new RingBuffer(m_bufsize); + + m_scratch[c] = new float[m_bufsize]; + for (size_t i = 0; i < m_bufsize; ++i) { + m_scratch[c][i] = 0.f; + } + + m_inptrs[c] = 0; + } + + activateImpl(); +} + +RubberBandR3PitchShifter::~RubberBandR3PitchShifter() +{ + delete m_stretcher; + for (size_t c = 0; c < m_channels; ++c) { + delete m_outputBuffer[c]; + delete m_delayMixBuffer[c]; + delete[] m_scratch[c]; + } + delete[] m_outputBuffer; + delete[] m_delayMixBuffer; + delete[] m_inptrs; + delete[] m_scratch; + delete[] m_output; + delete[] m_input; +} + +#ifdef RB_PLUGIN_LADSPA + +LADSPA_Handle +RubberBandR3PitchShifter::instantiate(const LADSPA_Descriptor *desc, unsigned long rate) +{ + if (desc->PortCount == ladspaDescriptorMono.PortCount) { + return new RubberBandR3PitchShifter(rate, 1); + } else if (desc->PortCount == ladspaDescriptorStereo.PortCount) { + return new RubberBandR3PitchShifter(rate, 2); + } + return nullptr; +} + +#else + +LV2_Handle +RubberBandR3PitchShifter::instantiate(const LV2_Descriptor *desc, double rate, + const char *, const LV2_Feature *const *) +{ + if (rate < 1.0) { + std::cerr << "RubberBandR3PitchShifter::instantiate: invalid sample rate " + << rate << " provided" << std::endl; + return nullptr; + } + size_t srate = size_t(round(rate)); + if (std::string(desc->URI) == lv2DescriptorMono.URI) { + return new RubberBandR3PitchShifter(srate, 1); + } else if (std::string(desc->URI) == lv2DescriptorStereo.URI) { + return new RubberBandR3PitchShifter(srate, 2); + } else { + std::cerr << "RubberBandR3PitchShifter::instantiate: unrecognised URI " + << desc->URI << " requested" << std::endl; + return nullptr; + } +} + +#endif + +#ifdef RB_PLUGIN_LADSPA +void +RubberBandR3PitchShifter::connectPort(LADSPA_Handle handle, + unsigned long port, LADSPA_Data *location) +#else +void +RubberBandR3PitchShifter::connectPort(LV2_Handle handle, + uint32_t port, void *location) +#endif +{ + RubberBandR3PitchShifter *shifter = (RubberBandR3PitchShifter *)handle; + + float **ports[PortCountStereo] = { + &shifter->m_latency, + &shifter->m_cents, + &shifter->m_semitones, + &shifter->m_octaves, + &shifter->m_formant, + &shifter->m_wetDry, + &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) = shifter->getLatency(); + } +} + +#ifdef RB_PLUGIN_LADSPA +void +RubberBandR3PitchShifter::activate(LADSPA_Handle handle) +#else +void +RubberBandR3PitchShifter::activate(LV2_Handle handle) +#endif +{ + RubberBandR3PitchShifter *shifter = (RubberBandR3PitchShifter *)handle; + shifter->activateImpl(); +} + +#ifdef RB_PLUGIN_LADSPA +void +RubberBandR3PitchShifter::run(LADSPA_Handle handle, unsigned long samples) +#else +void +RubberBandR3PitchShifter::run(LV2_Handle handle, uint32_t samples) +#endif +{ + RubberBandR3PitchShifter *shifter = (RubberBandR3PitchShifter *)handle; + shifter->runImpl(samples); +} + +#ifdef RB_PLUGIN_LADSPA +void +RubberBandR3PitchShifter::deactivate(LADSPA_Handle handle) +#else +void +RubberBandR3PitchShifter::deactivate(LV2_Handle handle) +#endif +{ + activate(handle); // both functions just reset the plugin +} + +#ifdef RB_PLUGIN_LADSPA +void +RubberBandR3PitchShifter::cleanup(LADSPA_Handle handle) +#else +void +RubberBandR3PitchShifter::cleanup(LV2_Handle handle) +#endif +{ + delete (RubberBandR3PitchShifter *)handle; +} + +int +RubberBandR3PitchShifter::getLatency() const +{ + return m_reserve; +} + +void +RubberBandR3PitchShifter::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(); + } + + for (size_t c = 0; c < m_channels; ++c) { + m_delayMixBuffer[c]->reset(); + m_delayMixBuffer[c]->zero(getLatency()); + } + + for (size_t c = 0; c < m_channels; ++c) { + for (size_t i = 0; i < m_bufsize; ++i) { + m_scratch[c][i] = 0.f; + } + } + + m_minfill = 0; + + m_stretcher->process(m_scratch, m_reserve, false); +} + +void +RubberBandR3PitchShifter::updateRatio() +{ + // The octaves, semitones, and cents parameters are supposed to be + // integral: we want to enforce that, just to avoid + // inconsistencies between hosts if some respect the hints more + // than others + +#ifdef RB_PLUGIN_LADSPA + + // But we don't want to change the long-standing behaviour of the + // LADSPA plugin, so let's leave this as-is and only do "the right + // thing" for LV2 + 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); + +#else + + // LV2 + + double octaves = round(m_octaves ? *m_octaves : 0.0); + if (octaves < -2.0) octaves = -2.0; + if (octaves > 2.0) octaves = 2.0; + + double semitones = round(m_semitones ? *m_semitones : 0.0); + if (semitones < -12.0) semitones = -12.0; + if (semitones > 12.0) semitones = 12.0; + + double cents = round(m_cents ? *m_cents : 0.0); + if (cents < -100.0) cents = -100.0; + if (cents > 100.0) cents = 100.0; + + m_ratio = pow(2.0, + octaves + + semitones / 12.0 + + cents / 1200.0); +#endif +} + +void +RubberBandR3PitchShifter::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 +RubberBandR3PitchShifter::runImpl(uint32_t insamples) +{ + for (size_t c = 0; c < m_channels; ++c) { + m_delayMixBuffer[c]->write(m_input[c], insamples); + } + + size_t 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) { + + size_t block = m_blockSize; + if (offset + block > insamples) { + block = insamples - offset; + } + + runImpl(block, offset); + + offset += block; + } + + float mix = 0.0; + if (m_wetDry) mix = *m_wetDry; + + for (size_t c = 0; c < m_channels; ++c) { + if (mix > 0.0) { + for (size_t i = 0; i < insamples; ++i) { + float dry = m_delayMixBuffer[c]->readOne(); + m_output[c][i] *= (1.0 - mix); + m_output[c][i] += dry * mix; + } + } else { + m_delayMixBuffer[c]->skip(insamples); + } + } +} + +void +RubberBandR3PitchShifter::runImpl(uint32_t insamples, uint32_t offset) +{ + updateRatio(); + if (m_ratio != m_prevRatio) { + m_stretcher->setPitchScale(m_ratio); + m_prevRatio = m_ratio; + } + + if (m_latency) { + *m_latency = getLatency(); + } + + updateFormant(); + + const int samples = insamples; + int processed = 0; + size_t outTotal = 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) { + m_inptrs[c] = &(m_input[c][offset + processed]); + } + + m_stretcher->process(m_inptrs, inchunk, false); + + processed += inchunk; + + int avail = m_stretcher->available(); + int writable = m_outputBuffer[0]->getWriteSpace(); + + int outchunk = avail; + if (outchunk > writable) { + cerr << "RubberBandR3PitchShifter::runImpl: buffer is not large enough: size = " << m_outputBuffer[0]->getSize() << ", chunk = " << outchunk << ", space = " << writable << " (buffer contains " << m_outputBuffer[0]->getReadSpace() << " unread)" << endl; + outchunk = writable; + } + + size_t actual = m_stretcher->retrieve(m_scratch, outchunk); + outTotal += actual; + + for (size_t c = 0; c < m_channels; ++c) { + m_outputBuffer[c]->write(m_scratch[c], actual); + } + } + + for (size_t c = 0; c < m_channels; ++c) { + int toRead = m_outputBuffer[c]->getReadSpace(); + if (toRead < samples && c == 0) { + cerr << "RubberBandR3PitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << toRead << endl; + } + int chunk = min(toRead, samples); + m_outputBuffer[c]->read(&(m_output[c][offset]), chunk); + } + + size_t fill = m_outputBuffer[0]->getReadSpace(); + if (fill < m_minfill || m_minfill == 0) { + m_minfill = fill; +// cerr << "minfill = " << m_minfill << endl; + } +} + diff --git a/ladspa-lv2/RubberBandR3PitchShifter.h b/ladspa-lv2/RubberBandR3PitchShifter.h new file mode 100644 index 0000000..52cfb60 --- /dev/null +++ b/ladspa-lv2/RubberBandR3PitchShifter.h @@ -0,0 +1,149 @@ +/* -*- 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-2022 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. +*/ + +#ifndef RUBBERBAND_R3_PITCH_SHIFTER_H +#define RUBBERBAND_R3_PITCH_SHIFTER_H + +#ifdef RB_PLUGIN_LADSPA +#ifdef RB_PLUGIN_LV2 +#error "Only one of RB_PLUGIN_LADSPA and RB_PLUGIN_LV2 may be defined at once" +#endif +#else +#ifndef RB_PLUGIN_LV2 +#error "Including code must define either RB_PLUGIN_LADSPA or RB_PLUGIN_LV2" +#endif +#endif + +#ifdef RB_PLUGIN_LADSPA +#include +#else +#include +#endif + +#include "common/RingBuffer.h" + +namespace RubberBand { +class RubberBandStretcher; +} + +class RubberBandR3PitchShifter +{ +public: +#ifdef RB_PLUGIN_LADSPA + static const LADSPA_Descriptor *getDescriptor(unsigned long index); +#else + static const LV2_Descriptor *getDescriptor(uint32_t index); +#endif + +protected: + RubberBandR3PitchShifter(int sampleRate, size_t channels); + ~RubberBandR3PitchShifter(); + + enum { + LatencyPort = 0, + CentsPort = 1, + SemitonesPort = 2, + OctavesPort = 3, + FormantPort = 4, + WetDryPort = 5, + InputPort1 = 6, + OutputPort1 = 7, + PortCountMono = OutputPort1 + 1, + InputPort2 = 8, + OutputPort2 = 9, + PortCountStereo = OutputPort2 + 1 + }; + +#ifdef RB_PLUGIN_LADSPA + static const char *const portNamesMono[PortCountMono]; + static const LADSPA_PortDescriptor portsMono[PortCountMono]; + static const LADSPA_PortRangeHint hintsMono[PortCountMono]; + + static const char *const portNamesStereo[PortCountStereo]; + static const LADSPA_PortDescriptor portsStereo[PortCountStereo]; + static const LADSPA_PortRangeHint hintsStereo[PortCountStereo]; + + static const LADSPA_Properties properties; + + static const LADSPA_Descriptor ladspaDescriptorMono; + static const LADSPA_Descriptor ladspaDescriptorStereo; + + static LADSPA_Handle instantiate(const LADSPA_Descriptor *, unsigned long); + static void connectPort(LADSPA_Handle, unsigned long, LADSPA_Data *); + static void activate(LADSPA_Handle); + static void run(LADSPA_Handle, unsigned long); + static void deactivate(LADSPA_Handle); + static void cleanup(LADSPA_Handle); + +#else + + static const LV2_Descriptor lv2DescriptorMono; + static const LV2_Descriptor lv2DescriptorStereo; + + static LV2_Handle instantiate(const LV2_Descriptor *, double, + const char *, const LV2_Feature *const *); + static void connectPort(LV2_Handle, uint32_t, void *); + static void activate(LV2_Handle); + static void run(LV2_Handle, uint32_t); + static void deactivate(LV2_Handle); + static void cleanup(LV2_Handle); + +#endif + + void activateImpl(); + void runImpl(uint32_t count); + void runImpl(uint32_t count, uint32_t offset); + void updateRatio(); + void updateFormant(); + + int getLatency() const; + + float **m_input; + float **m_output; + float *m_latency; + float *m_cents; + float *m_semitones; + float *m_octaves; + float *m_formant; + float *m_wetDry; + double m_ratio; + double m_prevRatio; + bool m_currentFormant; + + size_t m_blockSize; + size_t m_reserve; + size_t m_bufsize; + size_t m_minfill; + + RubberBand::RubberBandStretcher *m_stretcher; + RubberBand::RingBuffer **m_outputBuffer; + RubberBand::RingBuffer **m_delayMixBuffer; + float **m_scratch; + float **m_inptrs; + + int m_sampleRate; + size_t m_channels; +}; + + +#endif diff --git a/ladspa-lv2/ladspa-rubberband.cat b/ladspa-lv2/ladspa-rubberband.cat index 438e9a3..907081f 100644 --- a/ladspa-lv2/ladspa-rubberband.cat +++ b/ladspa-lv2/ladspa-rubberband.cat @@ -1,2 +1,4 @@ ladspa:ladspa-rubberband:rubberband-pitchshifter-mono::Frequency > Pitch shifters ladspa:ladspa-rubberband:rubberband-pitchshifter-stereo::Frequency > Pitch shifters +ladspa:ladspa-rubberband:rubberband-r3-pitchshifter-mono::Frequency > Pitch shifters +ladspa:ladspa-rubberband:rubberband-r3-pitchshifter-stereo::Frequency > Pitch shifters diff --git a/ladspa-lv2/libmain-ladspa.cpp b/ladspa-lv2/libmain-ladspa.cpp index 3d22289..518b87f 100644 --- a/ladspa-lv2/libmain-ladspa.cpp +++ b/ladspa-lv2/libmain-ladspa.cpp @@ -24,6 +24,7 @@ #define RB_PLUGIN_LADSPA 1 #undef RB_PLUGIN_LV2 #include "RubberBandPitchShifter.cpp" +#include "RubberBandR3PitchShifter.cpp" #include @@ -31,7 +32,11 @@ extern "C" { const LADSPA_Descriptor *ladspa_descriptor(unsigned long index) { - return RubberBandPitchShifter::getDescriptor(index); + if (index < 2) { + return RubberBandPitchShifter::getDescriptor(index); + } else { + return RubberBandR3PitchShifter::getDescriptor(index - 2); + } } } diff --git a/ladspa-lv2/libmain-lv2.cpp b/ladspa-lv2/libmain-lv2.cpp index 3e41ffb..57752f6 100644 --- a/ladspa-lv2/libmain-lv2.cpp +++ b/ladspa-lv2/libmain-lv2.cpp @@ -24,6 +24,7 @@ #define RB_PLUGIN_LV2 1 #undef RB_PLUGIN_LADSPA #include "RubberBandPitchShifter.cpp" +#include "RubberBandR3PitchShifter.cpp" #include @@ -32,7 +33,11 @@ extern "C" { LV2_SYMBOL_EXPORT const LV2_Descriptor *lv2_descriptor(uint32_t index) { - return RubberBandPitchShifter::getDescriptor(index); + if (index < 2) { + return RubberBandPitchShifter::getDescriptor(index); + } else { + return RubberBandR3PitchShifter::getDescriptor(index - 2); + } } } diff --git a/ladspa-lv2/rubberband.lv2/lv2-rubberband.ttl b/ladspa-lv2/rubberband.lv2/lv2-rubberband.ttl index d09d7aa..2f8a516 100644 --- a/ladspa-lv2/rubberband.lv2/lv2-rubberband.ttl +++ b/ladspa-lv2/rubberband.lv2/lv2-rubberband.ttl @@ -78,6 +78,16 @@ lv2:maximum 1 ; lv2:portProperty lv2:integer, lv2:toggled . +:formantPortR3 + a lv2:ControlPort, lv2:InputPort ; + lv2:index 4 ; + lv2:symbol "formant" ; + lv2:name "Formant Preserving" ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 1 ; + lv2:portProperty lv2:integer, lv2:toggled . + :wetDryPort a lv2:ControlPort, lv2:InputPort ; lv2:index 6 ; @@ -87,6 +97,15 @@ lv2:minimum 0 ; lv2:maximum 1 . +:wetDryPortR3 + a lv2:ControlPort, lv2:InputPort ; + lv2:index 5 ; + lv2:symbol "wetdry" ; + lv2:name "Wet-Dry Mix" ; + lv2:default 0 ; + lv2:minimum 0 ; + lv2:maximum 1 . + rubberband:mono_in_group a pg:MonoGroup, pg:InputGroup ; lv2:symbol "mono_in" ; @@ -146,6 +165,44 @@ rubberband:mono lv2:designation pg:center ; ] . +rubberband:r3mono + a doap:Project, lv2:Plugin, lv2:PitchPlugin ; + doap:name "Rubber Band R3 Mono Pitch Shifter" ; + doap:license ; + foaf:maker :maker ; + doap:developer :maker ; + doap:maintainer :maker ; + # Minor version will be 2x the Rubber Band API minor version, + # but this is an initial test release, so 0 + lv2:minorVersion 0 ; + lv2:microVersion 0 ; + lv2:optionalFeature lv2:hardRTCapable ; + pg:mainInput rubberband:mono_in_group ; + pg:mainOutput rubberband:mono_out_group ; + dc:replaces ; + lv2:port :latencyPort , + :centsPort , + :semitonesPort , + :octavesPort , + :formantPortR3 , + :wetDryPortR3 , + [ a lv2:AudioPort, lv2:InputPort ; + lv2:index 6 ; + lv2:symbol "input" ; + lv2:name "Input" ; + lv2:shortName "Input" ; + pg:group rubberband:mono_in_group ; + lv2:designation pg:center ; + ], [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 7 ; + lv2:symbol "output" ; + lv2:name "Output" ; + lv2:shortName "Output" ; + pg:group rubberband:mono_out_group ; + lv2:designation pg:center ; + ] . + rubberband:stereo a doap:Project, lv2:Plugin, lv2:PitchPlugin ; doap:name "Rubber Band Stereo Pitch Shifter" ; @@ -200,3 +257,56 @@ rubberband:stereo lv2:designation pg:right ; ] . +rubberband:r3stereo + a doap:Project, lv2:Plugin, lv2:PitchPlugin ; + doap:name "Rubber Band R3 Stereo Pitch Shifter" ; + doap:license ; + foaf:maker :maker ; + doap:developer :maker ; + doap:maintainer :maker ; + # Minor version will be 2x the Rubber Band API minor version, + # but this is an initial test release, so 0 + lv2:minorVersion 0 ; + lv2:microVersion 0 ; + lv2:optionalFeature lv2:hardRTCapable ; + pg:mainInput rubberband:stereo_in_group ; + pg:mainOutput rubberband:stereo_out_group ; + dc:replaces ; + lv2:port :latencyPort , + :centsPort , + :semitonesPort , + :octavesPort , + :formantPortR3 , + :wetDryPortR3 , + [ a lv2:AudioPort, lv2:InputPort ; + lv2:index 6 ; + lv2:symbol "input_l" ; + lv2:name "Input L" ; + lv2:shortName "Input L" ; + pg:group rubberband:stereo_in_group ; + lv2:designation pg:left ; + ], [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 7 ; + lv2:symbol "output_l" ; + lv2:name "Output L" ; + lv2:shortName "Output L" ; + pg:group rubberband:stereo_out_group ; + lv2:designation pg:left ; + ], [ a lv2:AudioPort, lv2:InputPort ; + lv2:index 8 ; + lv2:symbol "input_r" ; + lv2:name "Input R" ; + lv2:shortName "Input R" ; + pg:group rubberband:stereo_in_group ; + lv2:designation pg:right ; + ], [ + a lv2:AudioPort, lv2:OutputPort ; + lv2:index 9 ; + lv2:symbol "output_r" ; + lv2:name "Output R" ; + lv2:shortName "Output R" ; + pg:group rubberband:stereo_out_group ; + lv2:designation pg:right ; + ] . + diff --git a/ladspa-lv2/rubberband.lv2/manifest.ttl b/ladspa-lv2/rubberband.lv2/manifest.ttl index 5931138..ccba9dd 100644 --- a/ladspa-lv2/rubberband.lv2/manifest.ttl +++ b/ladspa-lv2/rubberband.lv2/manifest.ttl @@ -7,8 +7,18 @@ rubberband:mono lv2:binary ; rdfs:seeAlso . +rubberband:r3mono + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . + rubberband:stereo a lv2:Plugin ; lv2:binary ; rdfs:seeAlso . +rubberband:r3stereo + a lv2:Plugin ; + lv2:binary ; + rdfs:seeAlso . + diff --git a/src/finer/R3StretcherImpl.cpp b/src/finer/R3StretcherImpl.cpp index c266ca2..a20bf0e 100644 --- a/src/finer/R3StretcherImpl.cpp +++ b/src/finer/R3StretcherImpl.cpp @@ -190,6 +190,7 @@ R3StretcherImpl::setFormantOption(RubberBandStretcher::Options options) m_parameters.options |= options; } +//!!! unused? void R3StretcherImpl::setPitchOption(RubberBandStretcher::Options options) {