Files
librubberband/ladspa/RubberBandPitchShifter.cpp

564 lines
15 KiB
C++
Raw Normal View History

2007-11-06 21:41:16 +00:00
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
/*
2012-09-09 16:57:42 +01:00
Rubber Band Library
2007-11-06 21:41:16 +00:00
An audio time-stretching and pitch-shifting library.
2014-05-29 18:00:22 +01:00
Copyright 2007-2014 Particular Programs Ltd.
2012-09-09 16:57:42 +01:00
2007-11-06 21:41:16 +00:00
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.
2012-09-09 16:57:42 +01:00
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.
2007-11-06 21:41:16 +00:00
*/
#include "RubberBandPitchShifter.h"
#include "RubberBandStretcher.h"
#include <iostream>
#include <cmath>
using namespace RubberBand;
2008-07-06 19:24:53 +00:00
using std::cout;
using std::cerr;
using std::endl;
using std::min;
2007-11-06 21:41:16 +00:00
const char *const
RubberBandPitchShifter::portNamesMono[PortCountMono] =
{
2008-01-28 17:24:55 +00:00
"latency",
2007-11-06 21:41:16 +00:00
"Cents",
"Semitones",
"Octaves",
"Crispness",
2008-07-04 19:31:23 +00:00
"Formant Preserving",
2008-07-05 09:11:22 +00:00
"Faster",
2007-11-06 21:41:16 +00:00
"Input",
"Output"
};
const char *const
RubberBandPitchShifter::portNamesStereo[PortCountStereo] =
{
2008-01-28 17:24:55 +00:00
"latency",
2007-11-06 21:41:16 +00:00
"Cents",
"Semitones",
"Octaves",
"Crispness",
2008-07-04 19:31:23 +00:00
"Formant Preserving",
2008-07-05 09:11:22 +00:00
"Faster",
2007-11-06 21:41:16 +00:00
"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,
2008-07-04 19:31:23 +00:00
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
2007-11-06 21:41:16 +00:00
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,
2008-07-04 19:31:23 +00:00
LADSPA_PORT_INPUT | LADSPA_PORT_CONTROL,
2007-11-06 21:41:16 +00:00
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] =
{
2008-07-04 14:19:32 +00:00
{ 0, 0, 0 }, // latency
{ LADSPA_HINT_DEFAULT_0 | // cents
2007-11-06 21:41:16 +00:00
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE,
-100.0, 100.0 },
2008-07-04 14:19:32 +00:00
{ LADSPA_HINT_DEFAULT_0 | // semitones
2007-11-06 21:41:16 +00:00
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-12.0, 12.0 },
2008-07-04 14:19:32 +00:00
{ LADSPA_HINT_DEFAULT_0 | // octaves
2007-11-06 21:41:16 +00:00
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
2008-07-04 14:19:32 +00:00
-3.0, 3.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
0.0, 3.0 },
2008-07-04 14:19:32 +00:00
{ LADSPA_HINT_DEFAULT_0 | // formant preserving
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
2008-07-04 19:31:23 +00:00
{ LADSPA_HINT_DEFAULT_0 | // fast
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
2007-11-06 21:41:16 +00:00
{ 0, 0, 0 },
{ 0, 0, 0 }
};
const LADSPA_PortRangeHint
RubberBandPitchShifter::hintsStereo[PortCountStereo] =
{
2008-07-04 14:19:32 +00:00
{ 0, 0, 0 }, // latency
{ LADSPA_HINT_DEFAULT_0 | // cents
2007-11-06 21:41:16 +00:00
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE,
-100.0, 100.0 },
2008-07-04 14:19:32 +00:00
{ LADSPA_HINT_DEFAULT_0 | // semitones
2007-11-06 21:41:16 +00:00
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
-12.0, 12.0 },
2008-07-04 14:19:32 +00:00
{ LADSPA_HINT_DEFAULT_0 | // octaves
2007-11-06 21:41:16 +00:00
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
2008-07-04 14:19:32 +00:00
-3.0, 3.0 },
{ LADSPA_HINT_DEFAULT_MAXIMUM | // crispness
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_INTEGER,
0.0, 3.0 },
2008-07-04 14:19:32 +00:00
{ LADSPA_HINT_DEFAULT_0 | // formant preserving
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
2008-07-04 19:31:23 +00:00
{ LADSPA_HINT_DEFAULT_0 | // fast
LADSPA_HINT_BOUNDED_BELOW |
LADSPA_HINT_BOUNDED_ABOVE |
LADSPA_HINT_TOGGLED,
0.0, 1.0 },
2007-11-06 21:41:16 +00:00
{ 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
2008-06-04 20:43:22 +00:00
"Breakfast Quay",
"GPL",
2007-11-06 21:41:16 +00:00
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
2008-06-04 20:43:22 +00:00
"Breakfast Quay",
"GPL",
2007-11-06 21:41:16 +00:00
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),
2008-06-09 20:46:37 +00:00
m_formant(0),
2008-07-04 19:31:23 +00:00
m_fast(0),
2007-11-06 21:41:16 +00:00
m_ratio(1.0),
m_prevRatio(1.0),
m_currentCrispness(-1),
2008-06-09 20:46:37 +00:00
m_currentFormant(false),
2008-07-04 19:31:23 +00:00
m_currentFast(false),
2008-07-04 14:19:32 +00:00
m_blockSize(1024),
m_reserve(1024),
2008-07-06 19:24:53 +00:00
m_minfill(0),
m_stretcher(new RubberBandStretcher
2007-11-06 21:41:16 +00:00
(sampleRate, channels,
2008-07-04 14:19:32 +00:00
RubberBandStretcher::OptionProcessRealTime |
RubberBandStretcher::OptionPitchHighConsistency)),
2007-11-06 21:41:16 +00:00
m_sampleRate(sampleRate),
m_channels(channels)
{
for (size_t c = 0; c < m_channels; ++c) {
2008-07-04 14:19:32 +00:00
2007-11-06 21:41:16 +00:00
m_input[c] = 0;
m_output[c] = 0;
2008-07-04 14:19:32 +00:00
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;
2007-11-06 21:41:16 +00:00
}
2008-07-04 14:19:32 +00:00
activateImpl();
2007-11-06 21:41:16 +00:00
}
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,
2008-06-09 20:46:37 +00:00
&shifter->m_formant,
2008-07-04 19:31:23 +00:00
&shifter->m_fast,
&shifter->m_input[0],
2007-11-06 21:41:16 +00:00
&shifter->m_output[0],
&shifter->m_input[1],
&shifter->m_output[1]
};
2008-07-04 19:31:23 +00:00
if (shifter->m_channels == 1) {
if (port >= PortCountMono) return;
} else {
if (port >= PortCountStereo) return;
}
2007-11-06 21:41:16 +00:00
*ports[port] = (float *)location;
2008-07-04 14:19:32 +00:00
if (shifter->m_latency) {
*(shifter->m_latency) =
float(shifter->m_stretcher->getLatency() + shifter->m_reserve);
}
2007-11-06 21:41:16 +00:00
}
void
RubberBandPitchShifter::activate(LADSPA_Handle handle)
{
RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle;
2008-07-04 14:19:32 +00:00
shifter->activateImpl();
}
void
RubberBandPitchShifter::activateImpl()
{
updateRatio();
m_prevRatio = m_ratio;
m_stretcher->reset();
m_stretcher->setPitchScale(m_ratio);
2008-07-04 19:31:23 +00:00
for (size_t c = 0; c < m_channels; ++c) {
2008-07-04 14:19:32 +00:00
m_outputBuffer[c]->reset();
m_outputBuffer[c]->zero(m_reserve);
}
2008-07-06 19:24:53 +00:00
m_minfill = 0;
2008-07-04 14:19:32 +00:00
// prime stretcher
2008-07-06 19:24:53 +00:00
// 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);
// }
// }
2007-11-06 21:41:16 +00:00
}
void
RubberBandPitchShifter::run(LADSPA_Handle handle, unsigned long samples)
{
RubberBandPitchShifter *shifter = (RubberBandPitchShifter *)handle;
shifter->runImpl(samples);
}
void
RubberBandPitchShifter::updateRatio()
{
2008-07-04 14:19:32 +00:00
double oct = (m_octaves ? *m_octaves : 0.0);
oct += (m_semitones ? *m_semitones : 0.0) / 12;
oct += (m_cents ? *m_cents : 0.0) / 1200;
2007-11-06 21:41:16 +00:00
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;
}
2008-06-09 20:46:37 +00:00
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;
}
2008-07-04 19:31:23 +00:00
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;
}
2007-11-06 21:41:16 +00:00
void
RubberBandPitchShifter::runImpl(unsigned long insamples)
2008-07-04 14:19:32 +00:00
{
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;
2008-07-04 19:31:23 +00:00
if (block + offset > insamples) block = insamples - offset;
2008-07-04 14:19:32 +00:00
runImpl(block, offset);
offset += block;
}
}
void
RubberBandPitchShifter::runImpl(unsigned long insamples, unsigned long offset)
2007-11-06 21:41:16 +00:00
{
2008-07-06 19:24:53 +00:00
// cerr << "RubberBandPitchShifter::runImpl(" << insamples << ")" << endl;
2007-11-06 21:41:16 +00:00
2008-07-04 14:19:32 +00:00
// static int incount = 0, outcount = 0;
2007-11-06 21:41:16 +00:00
updateRatio();
if (m_ratio != m_prevRatio) {
m_stretcher->setPitchScale(m_ratio);
m_prevRatio = m_ratio;
}
if (m_latency) {
2008-07-04 14:19:32 +00:00
*m_latency = float(m_stretcher->getLatency() + m_reserve);
2008-07-06 19:24:53 +00:00
// cerr << "latency = " << *m_latency << endl;
2007-11-06 21:41:16 +00:00
}
updateCrispness();
2008-06-09 20:46:37 +00:00
updateFormant();
2008-07-04 19:31:23 +00:00
updateFast();
2008-07-04 14:19:32 +00:00
const int samples = insamples;
2007-11-06 21:41:16 +00:00
int processed = 0;
size_t outTotal = 0;
float *ptrs[2];
2008-07-04 19:31:23 +00:00
int rs = m_outputBuffer[0]->getReadSpace();
2008-07-06 19:24:53 +00:00
if (rs < int(m_minfill)) {
// cerr << "temporary expansion (have " << rs << ", want " << m_reserve << ")" << endl;
2008-07-04 14:19:32 +00:00
m_stretcher->setTimeRatio(1.1); // fill up temporarily
2008-07-04 19:31:23 +00:00
} else if (rs > 8192) {
2008-07-06 19:24:53 +00:00
// cerr << "temporary reduction (have " << rs << ", want " << m_reserve << ")" << endl;
2008-07-04 14:19:32 +00:00
m_stretcher->setTimeRatio(0.9); // reduce temporarily
} else {
m_stretcher->setTimeRatio(1.0);
}
2007-11-06 21:41:16 +00:00
while (processed < samples) {
2008-07-04 14:19:32 +00:00
// 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
2007-11-06 21:41:16 +00:00
int toCauseProcessing = m_stretcher->getSamplesRequired();
2008-07-06 19:24:53 +00:00
int inchunk = min(samples - processed, toCauseProcessing);
2007-11-06 21:41:16 +00:00
for (size_t c = 0; c < m_channels; ++c) {
2008-07-04 14:19:32 +00:00
ptrs[c] = &(m_input[c][offset + processed]);
2007-11-06 21:41:16 +00:00
}
m_stretcher->process(ptrs, inchunk, false);
processed += inchunk;
int avail = m_stretcher->available();
int writable = m_outputBuffer[0]->getWriteSpace();
2008-07-06 19:24:53 +00:00
int outchunk = min(avail, writable);
2007-11-06 21:41:16 +00:00
size_t actual = m_stretcher->retrieve(m_scratch, outchunk);
outTotal += actual;
2008-07-04 14:19:32 +00:00
// incount += inchunk;
// outcount += actual;
2008-07-06 19:24:53 +00:00
// cout << "avail: " << avail << ", outchunk = " << outchunk;
// if (actual != outchunk) cout << " (" << actual << ")";
// cout << endl;
2007-11-06 21:41:16 +00:00
outchunk = actual;
for (size_t c = 0; c < m_channels; ++c) {
if (int(m_outputBuffer[c]->getWriteSpace()) < outchunk) {
2008-07-06 19:24:53 +00:00
cerr << "RubberBandPitchShifter::runImpl: buffer overrun: chunk = " << outchunk << ", space = " << m_outputBuffer[c]->getWriteSpace() << endl;
2007-11-06 21:41:16 +00:00
}
m_outputBuffer[c]->write(m_scratch[c], outchunk);
}
}
for (size_t c = 0; c < m_channels; ++c) {
2008-07-04 14:19:32 +00:00
int toRead = m_outputBuffer[c]->getReadSpace();
if (toRead < samples && c == 0) {
2008-07-06 19:24:53 +00:00
cerr << "RubberBandPitchShifter::runImpl: buffer underrun: required = " << samples << ", available = " << toRead << endl;
2007-11-06 21:41:16 +00:00
}
2008-07-06 19:24:53 +00:00
int chunk = min(toRead, samples);
2008-07-04 14:19:32 +00:00
m_outputBuffer[c]->read(&(m_output[c][offset]), chunk);
}
2008-07-06 19:24:53 +00:00
if (m_minfill == 0) {
m_minfill = m_outputBuffer[0]->getReadSpace();
// cerr << "minfill = " << m_minfill << endl;
}
2007-11-06 21:41:16 +00:00
}
void
RubberBandPitchShifter::deactivate(LADSPA_Handle handle)
{
activate(handle); // both functions just reset the plugin
}
void
RubberBandPitchShifter::cleanup(LADSPA_Handle handle)
{
delete (RubberBandPitchShifter *)handle;
}