Files
librubberband/src/faster/R2Stretcher.h

262 lines
7.9 KiB
C++

/* -*- 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_STRETCHERIMPL_H
#define RUBBERBAND_STRETCHERIMPL_H
#include "../../rubberband/RubberBandStretcher.h"
#include "../common/Window.h"
#include "../common/FFT.h"
#include "../common/RingBuffer.h"
#include "../common/Scavenger.h"
#include "../common/Thread.h"
#include "../common/Log.h"
#include "../common/sysutils.h"
#include "SincWindow.h"
#include "CompoundAudioCurve.h"
#include <set>
#include <algorithm>
namespace RubberBand
{
class AudioCurveCalculator;
class StretchCalculator;
class R2Stretcher
{
public:
R2Stretcher(size_t sampleRate, size_t channels,
RubberBandStretcher::Options options,
double initialTimeRatio, double initialPitchScale,
Log log);
~R2Stretcher();
void reset();
void setTimeRatio(double ratio);
void setPitchScale(double scale);
double getTimeRatio() const;
double getPitchScale() const;
size_t getPreferredStartPad() const;
size_t getStartDelay() const;
void setTransientsOption(RubberBandStretcher::Options);
void setDetectorOption(RubberBandStretcher::Options);
void setPhaseOption(RubberBandStretcher::Options);
void setFormantOption(RubberBandStretcher::Options);
void setPitchOption(RubberBandStretcher::Options);
void setExpectedInputDuration(size_t samples);
void setMaxProcessSize(size_t samples);
void setKeyFrameMap(const std::map<size_t, size_t> &);
size_t getSamplesRequired() const;
void study(const float *const *input, size_t samples, bool final);
void process(const float *const *input, size_t samples, bool final);
int available() const;
size_t retrieve(float *const *output, size_t samples) const;
float getFrequencyCutoff(int n) const;
void setFrequencyCutoff(int n, float f);
size_t getInputIncrement() const {
return m_increment;
}
std::vector<int> getOutputIncrements() const;
std::vector<float> getPhaseResetCurve() const;
std::vector<int> getExactTimePoints() const;
size_t getChannelCount() const {
return m_channels;
}
void calculateStretch();
void setDebugLevel(int level);
protected:
size_t m_sampleRate;
size_t m_channels;
void prepareChannelMS(size_t channel, const float *const *inputs,
size_t offset, size_t samples, float *prepared);
size_t consumeChannel(size_t channel, const float *const *inputs,
size_t offset, size_t samples, bool final);
void processChunks(size_t channel, bool &any, bool &last);
bool processOneChunk(); // across all channels, for real time use
bool processChunkForChannel(size_t channel, size_t phaseIncrement,
size_t shiftIncrement, bool phaseReset);
bool testInbufReadSpace(size_t channel);
void calculateIncrements(size_t &phaseIncrement,
size_t &shiftIncrement, bool &phaseReset);
bool getIncrements(size_t channel, size_t &phaseIncrement,
size_t &shiftIncrement, bool &phaseReset);
void analyseChunk(size_t channel);
void modifyChunk(size_t channel, size_t outputIncrement, bool phaseReset);
void formantShiftChunk(size_t channel);
void synthesiseChunk(size_t channel, size_t shiftIncrement);
void writeChunk(size_t channel, size_t shiftIncrement, bool last);
void calculateSizes();
void configure();
void reconfigure();
double getEffectiveRatio() const;
template <typename T, typename S>
void cutShiftAndFold(T *target, int targetSize,
S *src, // destructive to src
Window<float> *window) {
window->cut(src);
const int windowSize = window->getSize();
const int hs = targetSize / 2;
if (windowSize == targetSize) {
v_convert(target, src + hs, hs);
v_convert(target + hs, src, hs);
} else {
v_zero(target, targetSize);
int j = targetSize - windowSize/2;
while (j < 0) j += targetSize;
for (int i = 0; i < windowSize; ++i) {
target[j] += src[i];
if (++j == targetSize) j = 0;
}
}
}
bool resampleBeforeStretching() const;
double m_timeRatio;
double m_pitchScale;
// n.b. either m_fftSize is an integer multiple of m_windowSize,
// or vice versa
size_t m_fftSize;
size_t m_aWindowSize; //!!! or use m_awindow->getSize() throughout?
size_t m_sWindowSize; //!!! or use m_swindow->getSize() throughout?
size_t m_increment;
size_t m_outbufSize;
size_t m_maxProcessSize;
size_t m_expectedInputDuration;
#ifndef NO_THREADING
bool m_threaded;
#endif
bool m_realtime;
RubberBandStretcher::Options m_options;
Log m_log;
enum ProcessMode {
JustCreated,
Studying,
Processing,
Finished
};
ProcessMode m_mode;
std::map<size_t, Window<float> *> m_windows;
std::map<size_t, SincWindow<float> *> m_sincs;
Window<float> *m_awindow;
SincWindow<float> *m_afilter;
Window<float> *m_swindow;
FFT *m_studyFFT;
#ifndef NO_THREADING
Condition m_spaceAvailable;
class ProcessThread : public Thread
{
public:
ProcessThread(R2Stretcher *s, size_t c);
void run();
void signalDataAvailable();
void abandon();
size_t channel() { return m_channel; }
private:
R2Stretcher *m_s;
size_t m_channel;
Condition m_dataAvailable;
bool m_abandoning;
};
mutable Mutex m_threadSetMutex;
typedef std::set<ProcessThread *> ThreadSet;
ThreadSet m_threadSet;
#if defined(HAVE_IPP) && !defined(NO_THREADING) && !defined(USE_BQRESAMPLER) && !defined(USE_SPEEX) && !defined(HAVE_LIBSAMPLERATE)
// Exasperatingly, the IPP polyphase resampler does not appear to
// be thread-safe as advertised -- a good reason to prefer any of
// the alternatives
#define STRETCHER_IMPL_RESAMPLER_MUTEX_REQUIRED 1
Mutex m_resamplerMutex;
#endif
#endif // ! NO_THREADING
size_t m_inputDuration;
CompoundAudioCurve::Type m_detectorType;
std::vector<float> m_phaseResetDf;
std::vector<bool> m_silence;
int m_silentHistory;
class ChannelData;
std::vector<ChannelData *> m_channelData;
std::vector<int> m_outputIncrements;
mutable RingBuffer<int> m_lastProcessOutputIncrements;
mutable RingBuffer<float> m_lastProcessPhaseResetDf;
Scavenger<RingBuffer<float> > m_emergencyScavenger;
CompoundAudioCurve *m_phaseResetAudioCurve;
AudioCurveCalculator *m_silentAudioCurve;
StretchCalculator *m_stretchCalculator;
float m_freq0;
float m_freq1;
float m_freq2;
size_t m_baseFftSize;
float m_rateMultiple;
void writeOutput(RingBuffer<float> &to, float *from,
size_t qty, size_t &outCount, size_t theoreticalOut);
static const size_t m_defaultIncrement;
static const size_t m_defaultFftSize;
};
}
#endif