Files
librubberband/ladspa/RubberBandPitchShifter.cpp

555 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: */
/*
Rubber Band
An audio time-stretching and pitch-shifting library.
Copyright 2007-2010 Chris Cannam.
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.
*/
#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;
}