Add pre-pad for realtime mode (equivalent to the padding RB does internally in offline mode already)

This commit is contained in:
Chris Cannam
2021-10-14 13:28:29 +01:00
parent 674f44dcba
commit 05db791db3

View File

@@ -45,9 +45,6 @@
#include "../src/base/Profiler.h"
using namespace std;
using namespace RubberBand;
#ifdef _WIN32
using RubberBand::gettimeofday;
#endif
@@ -57,6 +54,11 @@ using RubberBand::usleep;
#define strdup _strdup
#endif
using RubberBand::RubberBandStretcher;
using std::cerr;
using std::endl;
double tempo_convert(const char *str)
{
char *d = strchr((char *)str, ':');
@@ -489,9 +491,6 @@ int main(int argc, char **argv)
return 1;
}
int ibs = 1024;
size_t channels = sfinfo.channels;
RubberBandStretcher::Options options = 0;
if (realtime) options |= RubberBandStretcher::OptionProcessRealTime;
if (precise) options |= RubberBandStretcher::OptionStretchPrecise;
@@ -569,6 +568,17 @@ int main(int argc, char **argv)
float gain = 1.f;
bool successful = false;
const size_t channels = sfinfo.channels;
const int bs = 1024;
float **cbuf = new float *[channels];
for (size_t c = 0; c < channels; ++c) {
cbuf[c] = new float[bs];
}
float *ibuf = new float[channels * bs];
int thisBlockSize;
while (!successful) { // we may have to repeat with a modified
// gain, if clipping occurs
successful = true;
@@ -577,12 +587,6 @@ int main(int argc, char **argv)
ratio, frequencyshift);
ts.setExpectedInputDuration(sfinfo.frames);
float *fbuf = new float[channels * ibs];
float **ibuf = new float *[channels];
for (size_t i = 0; i < channels; ++i) {
ibuf[i] = new float[ibs];
}
int frame = 0;
int percent = 0;
@@ -597,19 +601,17 @@ int main(int argc, char **argv)
while (frame < sfinfo.frames) {
int count = -1;
if ((count = sf_readf_float(sndfile, fbuf, ibs)) <= 0) break;
if ((count = sf_readf_float(sndfile, ibuf, bs)) <= 0) break;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < count; ++i) {
float value = fbuf[i * channels + c];
ibuf[c][i] = value;
cbuf[c][i] = ibuf[i * channels + c];
}
}
bool final = (frame + ibs >= sfinfo.frames);
bool final = (frame + bs >= sfinfo.frames);
ts.study(ibuf, count, final);
ts.study(cbuf, count, final);
int p = int((double(frame) * 100.0) / sfinfo.frames);
if (p > percent || frame == 0) {
@@ -619,7 +621,7 @@ int main(int argc, char **argv)
}
}
frame += ibs;
frame += bs;
}
if (!quiet) {
@@ -642,10 +644,35 @@ int main(int argc, char **argv)
countOut = 0;
bool clipping = false;
// The stretcher only pads the start in offline mode; to avoid
// a fade in at the start, we pad it manually in RT mode
int toDrop = 0;
if (realtime) {
toDrop = int(ts.getLatency());
int toPad = int(round(toDrop * frequencyshift));
if (debug > 0) {
cerr << "padding start with " << toPad
<< " samples in RT mode, will drop " << toDrop
<< " at output" << endl;
}
if (toPad > 0) {
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < bs; ++i) {
cbuf[c][i] = 0.f;
}
}
while (toPad > 0) {
int p = toPad;
if (p > bs) p = bs;
ts.process(cbuf, p, false);
toPad -= p;
}
}
}
while (frame < sfinfo.frames) {
int count = -1;
int thisBlockSize = ibs;
thisBlockSize = bs;
while (freqMapItr != freqMap.end()) {
size_t nextFreqFrame = freqMapItr->first;
@@ -667,7 +694,8 @@ int main(int argc, char **argv)
}
}
if ((count = sf_readf_float(sndfile, fbuf, thisBlockSize)) < 0) {
int count = -1;
if ((count = sf_readf_float(sndfile, ibuf, thisBlockSize)) < 0) {
break;
}
@@ -675,51 +703,84 @@ int main(int argc, char **argv)
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < count; ++i) {
float value = fbuf[i * channels + c];
ibuf[c][i] = value;
cbuf[c][i] = ibuf[i * channels + c];
}
}
bool final = (frame + thisBlockSize >= sfinfo.frames);
if (debug > 2) {
cerr << "count = " << count << ", ibs = " << thisBlockSize << ", frame = " << frame << ", frames = " << sfinfo.frames << ", final = " << final << endl;
cerr << "count = " << count << ", bs = " << thisBlockSize << ", frame = " << frame << ", frames = " << sfinfo.frames << ", final = " << final << endl;
}
ts.process(ibuf, count, final);
ts.process(cbuf, count, final);
int avail = ts.available();
if (debug > 1) cerr << "available = " << avail << endl;
if (avail > 0) {
float **obf = new float *[channels];
for (size_t i = 0; i < channels; ++i) {
obf[i] = new float[avail];
int avail;
while ((avail = ts.available()) > 0) {
if (debug > 1) {
cerr << "available = " << avail << endl;
}
ts.retrieve(obf, avail);
countOut += avail;
float *fobf = new float[channels * avail];
thisBlockSize = avail;
if (thisBlockSize > bs) {
thisBlockSize = bs;
}
if (toDrop > 0) {
int dropHere = toDrop;
if (dropHere > thisBlockSize) {
dropHere = thisBlockSize;
}
if (debug > 1) {
cerr << "toDrop = " << toDrop << ", dropping "
<< dropHere << " of " << avail << endl;
}
ts.retrieve(cbuf, dropHere);
toDrop -= dropHere;
avail -= dropHere;
continue;
}
if (debug > 2) {
cerr << "retrieving block of " << thisBlockSize << endl;
}
ts.retrieve(cbuf, thisBlockSize);
if (realtime && final) {
// (in offline mode the stretcher handles this itself)
size_t ideal = size_t(countIn * ratio);
if (debug > 2) {
cerr << "at end, ideal = " << ideal
<< ", countOut = " << countOut
<< ", thisBlockSize = " << thisBlockSize << endl;
}
if (countOut + thisBlockSize > ideal) {
thisBlockSize = ideal - countOut;
if (debug > 1) {
cerr << "truncated final block to " << thisBlockSize
<< endl;
}
}
}
countOut += thisBlockSize;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < avail; ++i) {
float value = gain * obf[c][i];
for (int i = 0; i < thisBlockSize; ++i) {
float value = gain * cbuf[c][i];
if (ignoreClipping) { // i.e. just clamp, don't bail out
if (value > 1.f) value = 1.f;
if (value < -1.f) value = -1.f;
} else {
if (value >= 1.f || value < -1.f) {
clipping = true;
gain = (0.999f / fabsf(obf[c][i]));
gain = (0.999f / fabsf(cbuf[c][i]));
}
}
fobf[i * channels + c] = value;
ibuf[i * channels + c] = value;
}
}
sf_writef_float(sndfileOut, fobf, avail);
delete[] fobf;
for (size_t i = 0; i < channels; ++i) {
delete[] obf[i];
}
delete[] obf;
sf_writef_float(sndfileOut, ibuf, thisBlockSize);
}
if (clipping) {
@@ -753,7 +814,7 @@ int main(int argc, char **argv)
}
}
frame += thisBlockSize;
frame += count;
}
if (!successful) {
@@ -765,48 +826,51 @@ int main(int argc, char **argv)
if (!quiet) {
cerr << "\r " << endl;
}
int avail;
while ((avail = ts.available()) >= 0) {
if (debug > 1) {
cerr << "(completing) available = " << avail << endl;
}
if (avail > 0) {
float **obf = new float *[channels];
for (size_t i = 0; i < channels; ++i) {
obf[i] = new float[avail];
}
ts.retrieve(obf, avail);
countOut += avail;
float *fobf = new float[channels * avail];
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < avail; ++i) {
float value = gain * obf[c][i];
if (value > 1.f) value = 1.f;
if (value < -1.f) value = -1.f;
fobf[i * channels + c] = value;
}
}
sf_writef_float(sndfileOut, fobf, avail);
delete[] fobf;
for (size_t i = 0; i < channels; ++i) {
delete[] obf[i];
}
delete[] obf;
if (avail == 0) {
if (realtime ||
(options & RubberBandStretcher::OptionThreadingNever)) {
break;
} else {
usleep(10000);
}
}
delete[] fbuf;
for (size_t i = 0; i < channels; ++i) delete[] ibuf[i];
delete[] ibuf;
thisBlockSize = avail;
if (thisBlockSize > bs) {
thisBlockSize = bs;
}
ts.retrieve(cbuf, thisBlockSize);
countOut += thisBlockSize;
for (size_t c = 0; c < channels; ++c) {
for (int i = 0; i < thisBlockSize; ++i) {
float value = gain * cbuf[c][i];
if (value > 1.f) value = 1.f;
if (value < -1.f) value = -1.f;
ibuf[i * channels + c] = value;
}
}
sf_writef_float(sndfileOut, ibuf, thisBlockSize);
}
}
delete[] ibuf;
for (size_t c = 0; c < channels; ++c) {
delete[] cbuf[c];
}
delete[] cbuf;
sf_close(sndfile);
sf_close(sndfileOut);
@@ -834,7 +898,7 @@ int main(int argc, char **argv)
cerr << "elapsed time: " << sec << " sec, in frames/sec: " << countIn/sec << ", out frames/sec: " << countOut/sec << endl;
}
Profiler::dump();
RubberBand::Profiler::dump();
return 0;
}