Add process mode; start on key-frame map

This commit is contained in:
Chris Cannam
2022-06-13 16:06:21 +01:00
parent 90ad1274d8
commit ac4072937e
3 changed files with 136 additions and 27 deletions

View File

@@ -140,7 +140,6 @@ void
RubberBandStretcher::setPitchOption(Options options)
{
if (m_d) m_d->setPitchOption(options);
else if (m_r3d) m_r3d->setPitchOption(options);
}
void
@@ -159,7 +158,7 @@ void
RubberBandStretcher::setKeyFrameMap(const std::map<size_t, size_t> &mapping)
{
if (m_d) m_d->setKeyFrameMap(mapping);
//!!!
else m_r3d->setKeyFrameMap(mapping);
}
size_t
@@ -174,7 +173,7 @@ RubberBandStretcher::study(const float *const *input, size_t samples,
bool final)
{
if (m_d) m_d->study(input, samples, final);
//!!!
else m_r3d->study(input, samples, final);
}
void

View File

@@ -42,7 +42,11 @@ R3StretcherImpl::R3StretcherImpl(Parameters parameters,
m_inhop(1),
m_prevOuthop(1),
m_startSkip(0),
m_draining(false)
m_studyInputDuration(0),
m_totalTargetDuration(0),
m_processInputDuration(0),
m_totalOutputDuration(0),
m_mode(ProcessMode::JustCreated)
{
double maxClassifierFrequency = 16000.0;
if (maxClassifierFrequency > m_parameters.sampleRate/2) {
@@ -92,7 +96,7 @@ R3StretcherImpl::R3StretcherImpl(Parameters parameters,
Resampler::Parameters resamplerParameters;
resamplerParameters.quality = Resampler::FastestTolerable;
if (m_parameters.options & RubberBandStretcher::OptionProcessRealTime) {
if (isRealTime()) {
resamplerParameters.dynamism = Resampler::RatioOftenChanging;
resamplerParameters.ratioChange = Resampler::SmoothRatioChange;
} else {
@@ -123,7 +127,7 @@ R3StretcherImpl::R3StretcherImpl(Parameters parameters,
// introduce more latency, and we don't want gaps when the ratio
// changes.
if (!(m_parameters.options & RubberBandStretcher::OptionProcessRealTime)) {
if (!isRealTime()) {
int pad = m_guideConfiguration.longestFftSize / 2;
for (int c = 0; c < m_parameters.channels; ++c) {
m_channelData[c]->inbuf->zero(pad);
@@ -163,6 +167,15 @@ R3StretcherImpl::ScaleData::synthesisWindowLength(int fftSize)
void
R3StretcherImpl::setTimeRatio(double ratio)
{
if (!isRealTime()) {
if (m_mode == ProcessMode::Studying ||
m_mode == ProcessMode::Processing) {
m_parameters.logger("R3StretcherImpl::setTimeRatio: Cannot set time ratio while studying or processing in non-RT mode");
return;
}
}
if (ratio == m_timeRatio) return;
m_timeRatio = ratio;
calculateHop();
}
@@ -170,6 +183,15 @@ R3StretcherImpl::setTimeRatio(double ratio)
void
R3StretcherImpl::setPitchScale(double scale)
{
if (!isRealTime()) {
if (m_mode == ProcessMode::Studying ||
m_mode == ProcessMode::Processing) {
m_parameters.logger("R3StretcherImpl::setTimeRatio: Cannot set pitch scale while studying or processing in non-RT mode");
return;
}
}
if (scale == m_pitchScale) return;
m_pitchScale = scale;
calculateHop();
}
@@ -177,6 +199,14 @@ R3StretcherImpl::setPitchScale(double scale)
void
R3StretcherImpl::setFormantScale(double scale)
{
if (!isRealTime()) {
if (m_mode == ProcessMode::Studying ||
m_mode == ProcessMode::Processing) {
m_parameters.logger("R3StretcherImpl::setTimeRatio: Cannot set formant scale while studying or processing in non-RT mode");
return;
}
}
m_formantScale = scale;
}
@@ -190,16 +220,19 @@ R3StretcherImpl::setFormantOption(RubberBandStretcher::Options options)
m_parameters.options |= options;
}
//!!! unused?
void
R3StretcherImpl::setPitchOption(RubberBandStretcher::Options options)
R3StretcherImpl::setKeyFrameMap(const std::map<size_t, size_t> &mapping)
{
int mask = (RubberBandStretcher::OptionPitchHighQuality |
RubberBandStretcher::OptionPitchHighSpeed |
RubberBandStretcher::OptionPitchHighConsistency);
m_parameters.options &= ~mask;
options &= mask;
m_parameters.options |= options;
if (isRealTime()) {
m_parameters.logger("R3StretcherImpl::setKeyFrameMap: Cannot specify key frame map in RT mode");
return;
}
if (m_mode == ProcessMode::Processing || m_mode == ProcessMode::Finished) {
m_parameters.logger("R3StretcherImpl::setKeyFrameMap: Cannot specify key frame map after process() has begun");
return;
}
m_keyFrameMap = mapping;
}
void
@@ -229,6 +262,14 @@ R3StretcherImpl::calculateHop()
// std::cout << "R3StretcherImpl::calculateHop: inhop = " << m_inhop << ", proposed outhop = " << proposedOuthop << ", mean outhop = " << m_inhop * ratio << std::endl;
}
void
R3StretcherImpl::updateRatioFromMap()
{
if (m_keyFrameMap.empty()) return;
auto itr = m_keyFrameMap.upper_bound(m_processInputDuration);
}
double
R3StretcherImpl::getTimeRatio() const
{
@@ -250,7 +291,7 @@ R3StretcherImpl::getFormantScale() const
size_t
R3StretcherImpl::getLatency() const
{
if (!(m_parameters.options & RubberBandStretcher::OptionProcessRealTime)) {
if (!isRealTime()) {
return 0;
} else {
double factor = m_pitchScale * 0.5;
@@ -280,6 +321,35 @@ R3StretcherImpl::reset()
m_prevInhop = m_inhop;
m_prevOuthop = int(round(m_inhop * getEffectiveRatio()));
m_studyInputDuration = 0;
m_totalTargetDuration = 0;
m_processInputDuration = 0;
m_totalOutputDuration = 0;
m_keyFrameMap.clear();
m_mode = ProcessMode::JustCreated;
}
void
R3StretcherImpl::study(const float *const *, size_t samples, bool)
{
if (isRealTime()) {
m_parameters.logger("R3StretcherImpl::study: Not meaningful in realtime mode");
return;
}
if (m_mode == ProcessMode::Processing || m_mode == ProcessMode::Finished) {
m_parameters.logger("R3StretcherImpl::study: Cannot study after processing");
return;
}
if (m_mode == ProcessMode::JustCreated) {
m_studyInputDuration = 0;
}
m_mode = ProcessMode::Studying;
m_studyInputDuration += samples;
}
size_t
@@ -301,10 +371,26 @@ R3StretcherImpl::process(const float *const *input, size_t samples, bool final)
{
//!!! todo: final
//!!! m_parameters.logger("process called");
if (m_mode == ProcessMode::Finished) {
m_parameters.logger("R3StretcherImpl::process: Cannot process again after final chunk");
return;
}
if (!isRealTime() && !m_keyFrameMap.empty()) {
if (m_mode == ProcessMode::Studying) {
m_totalTargetDuration =
round(m_studyInputDuration * getEffectiveRatio());
}
}
if (final) {
// m_parameters.logger("final = true");
m_draining = true;
// We don't distinguish between Finished and "draining, but
// haven't yet delivered all the samples" because the
// distinction is meaningless internally - it only affects
// whether available() finds any samples in the buffer
m_mode = ProcessMode::Finished;
} else {
m_mode = ProcessMode::Processing;
}
bool allConsumed = false;
@@ -324,6 +410,8 @@ R3StretcherImpl::process(const float *const *input, size_t samples, bool final)
m_channelData[c]->inbuf->write(input[c], samples);
}
m_processInputDuration += samples;
consume();
}
@@ -331,17 +419,18 @@ R3StretcherImpl::process(const float *const *input, size_t samples, bool final)
int
R3StretcherImpl::available() const
{
//!!! m_parameters.logger("available called");
int av = int(m_channelData[0]->outbuf->getReadSpace());
if (av == 0 && m_draining) return -1;
else return av;
if (av == 0 && m_mode == ProcessMode::Finished) {
return -1;
} else {
return av;
}
}
//!!! __attribute__((annotate("realtime")))
size_t
R3StretcherImpl::retrieve(float *const *output, size_t samples) const
{
//!!! m_parameters.logger("retrieve called");
size_t got = samples;
for (size_t c = 0; c < m_parameters.channels; ++c) {
@@ -411,7 +500,7 @@ R3StretcherImpl::consume()
int readSpace = m_channelData.at(0)->inbuf->getReadSpace();
if (readSpace < longest) {
if (m_draining) {
if (m_mode == ProcessMode::Finished) {
if (readSpace == 0) {
break;
}
@@ -475,7 +564,7 @@ R3StretcherImpl::consume()
m_channelAssembly.mixdown.data(),
outhop,
1.0 / m_pitchScale,
m_draining && readSpace < longest);
m_mode == ProcessMode::Finished && readSpace < longest);
}
// Emit
@@ -490,7 +579,7 @@ R3StretcherImpl::consume()
int readSpace = cd->inbuf->getReadSpace();
if (readSpace < inhop) {
// This should happen only when draining
// This should happen only when draining (Finished)
cd->inbuf->skip(readSpace);
} else {
cd->inbuf->skip(inhop);

View File

@@ -75,9 +75,11 @@ public:
double getPitchScale() const;
double getFormantScale() const;
void setFormantOption(RubberBandStretcher::Options);
void setPitchOption(RubberBandStretcher::Options);
void setKeyFrameMap(const std::map<size_t, size_t> &);
void setFormantOption(RubberBandStretcher::Options);
void study(const float *const *input, size_t samples, bool final);
size_t getSamplesRequired() const;
void process(const float *const *input, size_t samples, bool final);
int available() const;
@@ -280,10 +282,24 @@ protected:
int m_prevInhop;
int m_prevOuthop;
int m_startSkip;
bool m_draining;
size_t m_studyInputDuration;
size_t m_totalTargetDuration;
size_t m_processInputDuration;
size_t m_totalOutputDuration;
std::map<size_t, size_t> m_keyFrameMap;
enum class ProcessMode {
JustCreated,
Studying,
Processing,
Finished
};
ProcessMode m_mode;
void consume();
void calculateHop();
void updateRatioFromMap();
void analyseChannel(int channel, int inhop, int prevInhop, int prevOuthop);
void analyseFormant(int channel);
void adjustFormant(int channel);
@@ -294,6 +310,11 @@ protected:
return m_timeRatio * m_pitchScale;
}
bool isRealTime() const {
return m_parameters.options &
RubberBandStretcher::OptionProcessRealTime;
}
static void logCout(const std::string &message) {
std::cout << "RubberBandStretcher: " << message << std::endl;
}