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;
+}
+