Audacity 3.2.0
Classes | Public Types | Public Member Functions | Private Member Functions | Private Attributes | Static Private Attributes | List of all members
staffpad::TimeAndPitch Class Reference

#include <TimeAndPitch.h>

Collaboration diagram for staffpad::TimeAndPitch:
[legend]

Classes

struct  impl
 

Public Types

using ShiftTimbreCb = std::function< void(double factor, std::complex< float > *spectrum, const float *magnitude)>
 

Public Member Functions

 TimeAndPitch (int fftSize, bool reduceImaging=true, ShiftTimbreCb shiftTimbreCb={})
 
 ~TimeAndPitch ()
 
void setup (int numChannels, int maxBlockSize)
 
void setTimeStretchAndPitchFactor (double timeStretch, double pitchFactor)
 
int getSamplesToNextHop () const
 
int getNumAvailableOutputSamples () const
 
void feedAudio (const float *const *in_smp, int numSamples)
 
void retrieveAudio (float *const *out_smp, int numSamples)
 
void processPitchShift (float *const *smp, int numSamples, double pitchFactor)
 
int getLatencySamples () const
 
int getLatencySamplesForStretchRatio (float timeStretch) const
 
void reset ()
 

Private Member Functions

void _process_hop (int hop_a, int hop_s)
 process one hop/chunk in _fft_timeSeries and add the result to output circular buffer More...
 
template<int num_channels>
void _time_stretch (float hop_a, float hop_s)
 
void _applyImagingReduction ()
 

Private Attributes

const int fftSize
 
std::shared_ptr< impld
 
const bool _reduceImaging
 
const ShiftTimbreCb _shiftTimbreCb
 
int _numChannels = 1
 
int _maxBlockSize = 1024
 
double _resampleReadPos = 0.0
 
int _availableOutputSamples = 0
 
int _numBins = fftSize / 2 + 1
 
double _overlap_a = overlap
 
int _analysis_hop_counter = 0
 
double _expectedPhaseChangePerBinPerSample = 0.01
 
double _timeStretch = 1.0
 
double _pitchFactor = 1.0
 
int _outBufferWriteOffset = 0
 

Static Private Attributes

static constexpr int overlap = 4
 
static constexpr bool normalize_window = true
 
static constexpr bool modulate_synthesis_hop = true
 

Detailed Description

Definition at line 13 of file TimeAndPitch.h.

Member Typedef Documentation

◆ ShiftTimbreCb

using staffpad::TimeAndPitch::ShiftTimbreCb = std::function<void( double factor, std::complex<float>* spectrum, const float* magnitude)>

magnitude may be null, in which case the callback should re-compute it.

Definition at line 19 of file TimeAndPitch.h.

Constructor & Destructor Documentation

◆ TimeAndPitch()

staffpad::TimeAndPitch::TimeAndPitch ( int  fftSize,
bool  reduceImaging = true,
ShiftTimbreCb  shiftTimbreCb = {} 
)

Definition at line 90 of file TimeAndPitch.cpp.

93 , _reduceImaging(reduceImaging)
94 , _shiftTimbreCb(std::move(shiftTimbreCb))
95{
96}
const ShiftTimbreCb _shiftTimbreCb
Definition: TimeAndPitch.h:101

◆ ~TimeAndPitch()

staffpad::TimeAndPitch::~TimeAndPitch ( )

Definition at line 98 of file TimeAndPitch.cpp.

99{
100 // Here for ~std::shared_ptr<impl>() to know ~impl()
101}

Member Function Documentation

◆ _applyImagingReduction()

void staffpad::TimeAndPitch::_applyImagingReduction ( )
private

Definition at line 342 of file TimeAndPitch.cpp.

343{
344 // Upsampling brings spectral components down, including those that were
345 // beyond the Nyquist. From `imagingBeginBin`, we have a mirroring of the
346 // spectrum, which, if not lowpass-filtered by the resampler, may yield
347 // an aliasing-like quality if what is mirrored is a harmonic series. A
348 // simple thing to do against that is to scramble the phase values there.
349 // This way we preserve natural energy fluctuations, but the artifact
350 // isn't noticeable as much anymore, because much more noise-like, which
351 // is what one typically gets at those frequencies.
352 constexpr auto alignment = 64 / sizeof(float);
353 const int imagingBeginBin =
354 std::ceil((fftSize / 2 * _pitchFactor + 1) / alignment) * alignment;
355
356 if (imagingBeginBin >= _numBins)
357 return;
358
359 const auto n = _numBins - imagingBeginBin;
360 auto pSpec = d->spectrum.getPtr(0);
361 auto pRand = d->random_phases.getPtr(0);
362 vo::rotate(nullptr, pRand, pSpec + imagingBeginBin, n);
363 // Just rotating the random phase vector to produce pseudo-random
364 // sequences of phases.
365 std::uniform_int_distribution<size_t> dis(0, n - 1);
366 const auto middle = dis(d->randomGenerator);
367 std::rotate(pRand, pRand + middle, pRand + n);
368}
std::shared_ptr< impl > d
Definition: TimeAndPitch.h:97
void rotate(const float *oldPhase, const float *newPhase, std::complex< float > *dst, int32_t n)
Definition: VectorOps.h:140

References _numBins, _pitchFactor, d, fftSize, and staffpad::vo::rotate().

Referenced by _process_hop().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _process_hop()

void staffpad::TimeAndPitch::_process_hop ( int  hop_a,
int  hop_s 
)
private

process one hop/chunk in _fft_timeSeries and add the result to output circular buffer

Definition at line 371 of file TimeAndPitch.cpp.

372{
373 if (d->exact_hop_a != d->exact_hop_s)
374 {
375 if (_numChannels == 2)
376 _lr_to_ms(d->fft_timeseries.getPtr(0), d->fft_timeseries.getPtr(1), fftSize);
377
378 for (int ch = 0; ch < _numChannels; ++ch)
379 {
380 vo::multiply(d->fft_timeseries.getPtr(ch), d->cosWindow.getPtr(0), d->fft_timeseries.getPtr(ch), fftSize);
381 _fft_shift(d->fft_timeseries.getPtr(ch), fftSize);
382 }
383
384 // determine norm/phase
385 d->fft.forwardReal(d->fft_timeseries, d->spectrum);
386 // norms of the mid channel only (or sole channel) are needed in
387 // _time_stretch
388 vo::calcNorms(d->spectrum.getPtr(0), d->norm.getPtr(0), d->spectrum.getNumSamples());
389 for (int ch = 0; ch < _numChannels; ++ch)
390 vo::calcPhases(d->spectrum.getPtr(ch), d->phase.getPtr(ch), d->spectrum.getNumSamples());
391
392 if (_shiftTimbreCb)
394 1 / _pitchFactor, d->spectrum.getPtr(0), d->norm.getPtr(0));
395
396 if (_reduceImaging && _pitchFactor < 1.)
398
399 if (_numChannels == 1)
400 _time_stretch<1>((float)hop_a, (float)hop_s);
401 else if (_numChannels == 2)
402 _time_stretch<2>((float)hop_a, (float)hop_s);
403
404 for (int ch = 0; ch < _numChannels; ++ch)
405 _unwrapPhaseVec(d->phase_accum.getPtr(ch), _numBins);
406
407 for (int ch = 0; ch < _numChannels; ++ch)
408 vo::rotate(d->phase.getPtr(ch), d->phase_accum.getPtr(ch), d->spectrum.getPtr(ch),
409 d->spectrum.getNumSamples());
410 d->fft.inverseReal(d->spectrum, d->fft_timeseries);
411
412 for (int ch = 0; ch < _numChannels; ++ch)
413 vo::constantMultiply(d->fft_timeseries.getPtr(ch), 1.f / fftSize, d->fft_timeseries.getPtr(ch),
414 d->fft_timeseries.getNumSamples());
415
416 if (_numChannels == 2)
417 _ms_to_lr(d->fft_timeseries.getPtr(0), d->fft_timeseries.getPtr(1), fftSize);
418
419 for (int ch = 0; ch < _numChannels; ++ch)
420 {
421 _fft_shift(d->fft_timeseries.getPtr(ch), fftSize);
422 vo::multiply(d->fft_timeseries.getPtr(ch), d->cosWindow.getPtr(0), d->fft_timeseries.getPtr(ch), fftSize);
423 }
424 }
425 else
426 { // stretch factor == 1.0 => just apply window
427 for (int ch = 0; ch < _numChannels; ++ch)
428 vo::multiply(d->fft_timeseries.getPtr(ch), d->sqWindow.getPtr(0), d->fft_timeseries.getPtr(ch), fftSize);
429 }
430
431 {
432 float gainFact = float(_timeStretch * ((8.f / 3.f) / _overlap_a)); // overlap add normalization factor
433 for (int ch = 0; ch < _numChannels; ++ch)
434 d->outCircularBuffer[ch].writeAddBlockWithGain(_outBufferWriteOffset, fftSize, d->fft_timeseries.getPtr(ch),
435 gainFact);
436
438 d->normalizationBuffer.writeAddBlockWithGain(_outBufferWriteOffset, fftSize, d->sqWindow.getPtr(0), gainFact);
439 }
440 _outBufferWriteOffset += hop_s;
442}
static constexpr bool normalize_window
Definition: TimeAndPitch.h:89
void _fft_shift(float *v, int n)
rotate even-sized array by half its size to align fft phase at the center
void _lr_to_ms(float *ch1, float *ch2, int n)
void _ms_to_lr(float *ch1, float *ch2, int n)
void multiply(const T *src1, const T *src2, T *dst, int32_t n)
Definition: VectorOps.h:74
void calcPhases(const std::complex< float > *src, float *dst, int32_t n)
Definition: VectorOps.h:128
void calcNorms(const std::complex< float > *src, float *dst, int32_t n)
Definition: VectorOps.h:134
void constantMultiply(const T *src, T constant, T *dst, int32_t n)
Definition: VectorOps.h:60

References _applyImagingReduction(), _availableOutputSamples, staffpad::anonymous_namespace{TimeAndPitch.cpp}::_fft_shift(), staffpad::anonymous_namespace{TimeAndPitch.cpp}::_lr_to_ms(), staffpad::anonymous_namespace{TimeAndPitch.cpp}::_ms_to_lr(), _numBins, _numChannels, _outBufferWriteOffset, _overlap_a, _pitchFactor, _reduceImaging, _shiftTimbreCb, _timeStretch, staffpad::anonymous_namespace{TimeAndPitch.cpp}::_unwrapPhaseVec(), staffpad::vo::calcNorms(), staffpad::vo::calcPhases(), staffpad::vo::constantMultiply(), d, fftSize, staffpad::vo::multiply(), normalize_window, and staffpad::vo::rotate().

Referenced by feedAudio().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ _time_stretch()

template<int num_channels>
void staffpad::TimeAndPitch::_time_stretch ( float  hop_a,
float  hop_s 
)
private

Definition at line 239 of file TimeAndPitch.cpp.

240{
241 auto alpha = a_s / a_a; // this is the real stretch factor based on integer hop sizes
242
243 // Create a norm array
244 const auto* norms = d->norm.getPtr(0); // for stereo, just use the mid-channel
245 const auto* norms_last = d->last_norm.getPtr(0);
246
247 d->peak_index.clear();
248 d->trough_index.clear();
249 float lowest = norms[0];
250 int trough = 0;
251 if (norms_last[0] >= norms[1])
252 {
253 d->peak_index.emplace_back(0);
254 d->trough_index.emplace_back(0);
255 }
256 for (int i = 1; i < _numBins - 1; ++i)
257 {
258 if (norms_last[i] >= norms[i - 1] && norms_last[i] >= norms[i + 1])
259 {
260 d->peak_index.emplace_back(i);
261 d->trough_index.emplace_back(trough);
262 trough = i;
263 lowest = norms[i];
264 }
265 else if (norms[i] < lowest)
266 {
267 lowest = norms[i];
268 trough = i;
269 }
270 }
271 if (norms_last[_numBins - 1] > norms[_numBins - 2])
272 {
273 d->peak_index.emplace_back(_numBins - 1);
274 d->trough_index.emplace_back(trough);
275 }
276
277 if (d->peak_index.size() == 0)
278 {
279 // use max norm of last frame
280 int max_idx = 0;
281 float max_norm = 0.f;
282 vo::findMaxElement(norms_last, _numBins, max_idx, max_norm);
283 d->peak_index.emplace_back(max_idx);
284 }
285
286 const float** p = const_cast<const float**>(d->phase.getPtrs());
287 const float** p_l = const_cast<const float**>(d->last_phase.getPtrs());
288 float** acc = d->phase_accum.getPtrs();
289
290 float expChange_a = a_a * float(_expectedPhaseChangePerBinPerSample);
291 float expChange_s = a_s * float(_expectedPhaseChangePerBinPerSample);
292
293 int num_peaks = (int)d->peak_index.size();
294 // time integration for all peaks
295 for (int i = 0; i < num_peaks; ++i)
296 {
297 const int n = d->peak_index[i];
298 auto fn = float(n);
299
300 // determine phase from last frame
301 float fn_expChange_a = fn * expChange_a;
302 float fn_expChange_s = fn * expChange_s;
303
304 for (int ch = 0; ch < num_channels; ++ch)
305 acc[ch][n] = acc[ch][n] + alpha * _unwrapPhase(p[ch][n] - p_l[ch][n] - fn_expChange_a) + fn_expChange_s;
306 }
307
308 // go from first peak to 0
309 for (int n = d->peak_index[0]; n > 0; --n)
310 {
311 for (int ch = 0; ch < num_channels; ++ch)
312 acc[ch][n - 1] = acc[ch][n] - alpha * _unwrapPhase(p[ch][n] - p[ch][n - 1]);
313 }
314
315 // 'grow' from pairs of peaks to the lowest norm in between
316 for (int i = 0; i < num_peaks - 1; ++i)
317 {
318 const int mid = d->trough_index[i + 1];
319 for (int n = d->peak_index[i]; n < mid; ++n)
320 {
321 for (int ch = 0; ch < num_channels; ++ch)
322 acc[ch][n + 1] = acc[ch][n] + alpha * _unwrapPhase(p[ch][n + 1] - p[ch][n]);
323 }
324 for (int n = d->peak_index[i + 1]; n > mid + 1; --n)
325 {
326 for (int ch = 0; ch < num_channels; ++ch)
327 acc[ch][n - 1] = acc[ch][n] - alpha * _unwrapPhase(p[ch][n] - p[ch][n - 1]);
328 }
329 }
330
331 // last peak to the end
332 for (int n = d->peak_index[num_peaks - 1]; n < _numBins - 1; ++n)
333 {
334 for (int ch = 0; ch < num_channels; ++ch)
335 acc[ch][n + 1] = acc[ch][n] + alpha * _unwrapPhase(p[ch][n + 1] - p[ch][n]);
336 }
337
338 d->last_norm.assignSamples(d->norm);
339 d->last_phase.assignSamples(d->phase);
340}
static const auto fn
double _expectedPhaseChangePerBinPerSample
Definition: TimeAndPitch.h:112
void findMaxElement(const T *src, int32_t n, int32_t &maxIndex, T &maxValue)
Definition: VectorOps.h:87

References _expectedPhaseChangePerBinPerSample, _numBins, staffpad::anonymous_namespace{TimeAndPitch.cpp}::_unwrapPhase(), d, staffpad::vo::findMaxElement(), and fn.

Here is the call graph for this function:

◆ feedAudio()

void staffpad::TimeAndPitch::feedAudio ( const float *const *  in_smp,
int  numSamples 
)

Feed more input samples.

See also
getSamplesToNextHop() to know how much to feed to produce more output samples.

Definition at line 472 of file TimeAndPitch.cpp.

473{
474 assert(numSamples <= _maxBlockSize);
475 for (int ch = 0; ch < _numChannels; ++ch)
476 {
477 d->inResampleInputBuffer[ch].writeBlock(0, numSamples, input_smp[ch]);
478 d->inResampleInputBuffer[ch].advance(numSamples);
479 }
480 _resampleReadPos -= numSamples;
481
482 // determine integer hop size for the next hop.
483 // The error accumulators will make the value fluctuate by one to reach arbitrary scale
484 // ratios
485 if (d->exact_hop_s == 0.0) // this happens if feedAudio is called without setting up stretch factors
486 d->exact_hop_s = d->next_exact_hop_s;
487
488 const int hop_s = int(d->exact_hop_s + d->hop_s_err);
489 const int hop_a = int(d->exact_hop_a + d->hop_a_err);
490
491 double step = 0.0;
492 double read_pos = _resampleReadPos;
493 while (read_pos < 0.0)
494 {
495 auto int_pos = int(std::floor(read_pos));
496 float frac_pos = float(read_pos - int_pos);
497 for (int ch = 0; ch < _numChannels; ++ch)
498 {
499 float smp[6];
500 d->inResampleInputBuffer[ch].readBlock(int_pos - 6, 6, smp);
501 float s = (frac_pos == 0) ? smp[2] : lagrange6(smp, frac_pos);
502 d->inCircularBuffer[ch].writeOffset0(s);
503 d->inCircularBuffer[ch].advance(1);
504 }
506 step++;
507
508 if (_analysis_hop_counter >= hop_a)
509 {
510 _analysis_hop_counter -= hop_a;
511 d->hop_s_err += d->exact_hop_s - hop_s;
512 d->hop_a_err += d->exact_hop_a - hop_a;
513 for (int ch = 0; ch < _numChannels; ++ch)
514 d->inCircularBuffer[ch].readBlock(-fftSize, fftSize, d->fft_timeseries.getPtr(ch));
515 _process_hop(hop_a, hop_s);
516 }
517 read_pos = _resampleReadPos + step * _pitchFactor;
518 }
519 _resampleReadPos = read_pos;
520}
void _process_hop(int hop_a, int hop_s)
process one hop/chunk in _fft_timeSeries and add the result to output circular buffer
float lagrange6(const float(&smp)[6], float t)

References _analysis_hop_counter, _maxBlockSize, _numChannels, _pitchFactor, _process_hop(), _resampleReadPos, d, fftSize, and staffpad::anonymous_namespace{TimeAndPitch.cpp}::lagrange6().

Referenced by processPitchShift().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getLatencySamples()

int staffpad::TimeAndPitch::getLatencySamples ( ) const

Latency in input samples

Definition at line 558 of file TimeAndPitch.cpp.

559{
560 return fftSize - fftSize / overlap + 3; // 3 for resampling
561}
static constexpr int overlap
Definition: TimeAndPitch.h:88

References fftSize, and overlap.

Referenced by getLatencySamplesForStretchRatio().

Here is the caller graph for this function:

◆ getLatencySamplesForStretchRatio()

int staffpad::TimeAndPitch::getLatencySamplesForStretchRatio ( float  timeStretch) const

Latency in output samples, calculated on the given timeStretch factor

Definition at line 564 of file TimeAndPitch.cpp.

565{
566 const float coeff = (timeStretch < 1.f) ? (1.f / 3.f) : (2.f / 3.f);
567 return int(getLatencySamples() * (timeStretch * coeff + (1 - coeff)));
568}
int getLatencySamples() const

References getLatencySamples().

Here is the call graph for this function:

◆ getNumAvailableOutputSamples()

int staffpad::TimeAndPitch::getNumAvailableOutputSamples ( ) const

Tells the number of already processed output samples (after time-stretching) that are ready for consumption.

Definition at line 164 of file TimeAndPitch.cpp.

165{
167}

References _availableOutputSamples.

◆ getSamplesToNextHop()

int staffpad::TimeAndPitch::getSamplesToNextHop ( ) const

Return the ~number of samples until the next hop gets analysed (might be 1 or 2 too high). For extremely small stretch ratios, several analyses might be needed to produce samples, so you may have to repeat the process. Eventually, more output samples are produced.

Definition at line 158 of file TimeAndPitch.cpp.

159{
160 return std::max(0, int(std::ceil(d->exact_hop_a)) - _analysis_hop_counter +
161 1); // 1 extra to counter pitch factor error accumulation
162}

References _analysis_hop_counter, and d.

◆ processPitchShift()

void staffpad::TimeAndPitch::processPitchShift ( float *const *  smp,
int  numSamples,
double  pitchFactor 
)

Pitch-shift only in-place processing.

Definition at line 551 of file TimeAndPitch.cpp.

552{
553 setTimeStretchAndPitchFactor(1.0, pitchFactor);
554 feedAudio(smp, numSamples);
555 retrieveAudio(smp, numSamples);
556}
void setTimeStretchAndPitchFactor(double timeStretch, double pitchFactor)
void retrieveAudio(float *const *out_smp, int numSamples)
void feedAudio(const float *const *in_smp, int numSamples)

References feedAudio(), retrieveAudio(), and setTimeStretchAndPitchFactor().

Here is the call graph for this function:

◆ reset()

void staffpad::TimeAndPitch::reset ( )

Resets the internal state, discards any buffered input

Definition at line 169 of file TimeAndPitch.cpp.

170{
173 for (int ch = 0; ch < _numChannels; ++ch)
174 {
175 d->inResampleInputBuffer[ch].reset();
176 d->inCircularBuffer[ch].reset();
177 d->outCircularBuffer[ch].reset();
178 }
179 d->normalizationBuffer.reset();
180 d->last_norm.zeroOut();
181 d->last_phase.zeroOut();
182 d->phase_accum.zeroOut();
184 d->hop_a_err = 0.0;
185 d->hop_s_err = 0.0;
186 d->exact_hop_s = 0.0;
187 _resampleReadPos = 0.0;
188}

References _analysis_hop_counter, _availableOutputSamples, _numChannels, _outBufferWriteOffset, _resampleReadPos, and d.

Referenced by setup().

Here is the caller graph for this function:

◆ retrieveAudio()

void staffpad::TimeAndPitch::retrieveAudio ( float *const *  out_smp,
int  numSamples 
)

Retrieved shifted/stretched samples in output.

See also
getNumAvailableOutputSamples() to ask a valid amount of numSamples.

Definition at line 522 of file TimeAndPitch.cpp.

523{
524 assert(numSamples <= _maxBlockSize);
525 for (int ch = 0; ch < _numChannels; ++ch)
526 {
527 d->outCircularBuffer[ch].readAndClearBlock(0, numSamples, out_smp[ch]);
529 {
530 constexpr float curve =
531 4.f * 4.f; // the curve approximates 1/x over 1 but fades to 0 near 0 to avoid fade-in clicks
532 for (int i = 0; i < numSamples; ++i)
533 {
534 float x = d->normalizationBuffer.read(i);
535 out_smp[ch][i] *= x / (x * x + 1 / curve);
536 }
537 }
538 d->outCircularBuffer[ch].advance(numSamples);
539 }
540
541 d->normalizationBuffer.clearBlock(0, numSamples);
542 d->normalizationBuffer.advance(numSamples);
543
544 _outBufferWriteOffset -= numSamples;
545 _availableOutputSamples -= numSamples;
546
548 d->exact_hop_s = d->next_exact_hop_s;
549}
static constexpr bool modulate_synthesis_hop
Definition: TimeAndPitch.h:90

References _availableOutputSamples, _maxBlockSize, _numChannels, _outBufferWriteOffset, d, modulate_synthesis_hop, and normalize_window.

Referenced by processPitchShift().

Here is the caller graph for this function:

◆ setTimeStretchAndPitchFactor()

void staffpad::TimeAndPitch::setTimeStretchAndPitchFactor ( double  timeStretch,
double  pitchFactor 
)

Set independent time stretch and pitch factors (synchronously to processing thread). The factor change resolution follows the processing block size, with some approximation. As a general rule, smaller block sizes result in finer changes, although there's a lower bound due to internal windowed segmentation and OLA resynthesis.

Definition at line 444 of file TimeAndPitch.cpp.

445{
446 assert(timeScale > 0.0);
447 assert(pitchFactor > 0.0);
448 _pitchFactor = pitchFactor;
449 _timeStretch = timeScale * pitchFactor;
450
452 double overlap_s = overlap;
455 else
456 overlap_s /= _timeStretch;
457
458 d->exact_hop_a = double(fftSize) / _overlap_a;
460 {
461 d->exact_hop_s = double(fftSize) / overlap_s;
462 }
463 else
464 {
465 // switch after processing next block, unless it's the first one
466 d->next_exact_hop_s = double(fftSize) / overlap_s;
467 if (d->exact_hop_s == 0.0) // on first chunk set it immediately
468 d->exact_hop_s = d->next_exact_hop_s;
469 }
470}

References _overlap_a, _pitchFactor, _timeStretch, d, fftSize, modulate_synthesis_hop, and overlap.

Referenced by processPitchShift().

Here is the caller graph for this function:

◆ setup()

void staffpad::TimeAndPitch::setup ( int  numChannels,
int  maxBlockSize 
)

Setup at least once before processing.

Parameters
numChannelsMust be 1 or 2
maxBlockSizeThe caller's maximum block size, e.g. 1024 samples

Definition at line 103 of file TimeAndPitch.cpp.

104{
105 assert(numChannels == 1 || numChannels == 2);
106 _numChannels = numChannels;
107
108 d = std::make_unique<impl>(fftSize);
110 _numBins = fftSize / 2 + 1;
111
112 // audio sample buffers
113 d->fft_timeseries.setSize(_numChannels, fftSize);
114 int outBufferSize = fftSize + 2 * _maxBlockSize; // 1 block extra for safety
115 for (int ch = 0; ch < _numChannels; ++ch)
116 {
117 d->inResampleInputBuffer[ch].setSize(_maxBlockSize +
118 6); // needs additional history for resampling. more in the future
119 d->inCircularBuffer[ch].setSize(fftSize);
120 d->outCircularBuffer[ch].setSize(outBufferSize);
121 }
122 d->normalizationBuffer.setSize(outBufferSize);
123
124 // fft coefficient buffers
125 d->spectrum.setSize(_numChannels, _numBins);
126 d->norm.setSize(1, _numBins);
127 d->last_norm.setSize(1, _numBins);
128 d->phase.setSize(_numChannels, _numBins);
129 d->last_phase.setSize(_numChannels, _numBins);
130 d->phase_accum.setSize(_numChannels, _numBins);
131 d->random_phases.setSize(1, _numBins);
133 d->random_phases.getPtr(0), _numBins, d->randomGenerator);
134
136
137 // Cos window
138 // For perfect int-factor overlaps the first sample needs to be 0 and the last non-zero.
139 d->cosWindow.setSize(1, fftSize);
140 d->sqWindow.setSize(1, fftSize);
141 auto* w = d->cosWindow.getPtr(0);
142 auto* sqw = d->sqWindow.getPtr(0);
143 for (int i = 0; i < fftSize; ++i)
144 {
145 w[i] = -0.5f * std::cos(float(twoPi * (float)i / (float)fftSize)) + 0.5f;
146 sqw[i] = w[i] * w[i];
147 }
148
149 d->peak_index.reserve(_numBins);
150 d->trough_index.reserve(_numBins);
151
152 // force fft to allocate all buffers now. Currently there is no other way in the fft class
153 d->fft.forwardReal(d->fft_timeseries, d->spectrum);
154
155 reset();
156}
void generateRandomPhaseVector(float *dst, size_t size, std::mt19937 &gen)

References _expectedPhaseChangePerBinPerSample, _maxBlockSize, _numBins, _numChannels, d, fftSize, staffpad::anonymous_namespace{TimeAndPitch.cpp}::generateRandomPhaseVector(), anonymous_namespace{StaffPadTimeAndPitch.cpp}::maxBlockSize, reset(), and staffpad::anonymous_namespace{TimeAndPitch.cpp}::twoPi.

Here is the call graph for this function:

Member Data Documentation

◆ _analysis_hop_counter

int staffpad::TimeAndPitch::_analysis_hop_counter = 0
private

Definition at line 110 of file TimeAndPitch.h.

Referenced by feedAudio(), getSamplesToNextHop(), and reset().

◆ _availableOutputSamples

int staffpad::TimeAndPitch::_availableOutputSamples = 0
private

Definition at line 106 of file TimeAndPitch.h.

Referenced by _process_hop(), getNumAvailableOutputSamples(), reset(), and retrieveAudio().

◆ _expectedPhaseChangePerBinPerSample

double staffpad::TimeAndPitch::_expectedPhaseChangePerBinPerSample = 0.01
private

Definition at line 112 of file TimeAndPitch.h.

Referenced by _time_stretch(), and setup().

◆ _maxBlockSize

int staffpad::TimeAndPitch::_maxBlockSize = 1024
private

Definition at line 104 of file TimeAndPitch.h.

Referenced by feedAudio(), retrieveAudio(), and setup().

◆ _numBins

int staffpad::TimeAndPitch::_numBins = fftSize / 2 + 1
private

Definition at line 107 of file TimeAndPitch.h.

Referenced by _applyImagingReduction(), _process_hop(), _time_stretch(), and setup().

◆ _numChannels

int staffpad::TimeAndPitch::_numChannels = 1
private

Definition at line 103 of file TimeAndPitch.h.

Referenced by _process_hop(), feedAudio(), reset(), retrieveAudio(), and setup().

◆ _outBufferWriteOffset

int staffpad::TimeAndPitch::_outBufferWriteOffset = 0
private

Definition at line 116 of file TimeAndPitch.h.

Referenced by _process_hop(), reset(), and retrieveAudio().

◆ _overlap_a

double staffpad::TimeAndPitch::_overlap_a = overlap
private

Definition at line 108 of file TimeAndPitch.h.

Referenced by _process_hop(), and setTimeStretchAndPitchFactor().

◆ _pitchFactor

double staffpad::TimeAndPitch::_pitchFactor = 1.0
private

◆ _reduceImaging

const bool staffpad::TimeAndPitch::_reduceImaging
private

Definition at line 100 of file TimeAndPitch.h.

Referenced by _process_hop().

◆ _resampleReadPos

double staffpad::TimeAndPitch::_resampleReadPos = 0.0
private

Definition at line 105 of file TimeAndPitch.h.

Referenced by feedAudio(), and reset().

◆ _shiftTimbreCb

const ShiftTimbreCb staffpad::TimeAndPitch::_shiftTimbreCb
private

Definition at line 101 of file TimeAndPitch.h.

Referenced by _process_hop().

◆ _timeStretch

double staffpad::TimeAndPitch::_timeStretch = 1.0
private

Definition at line 113 of file TimeAndPitch.h.

Referenced by _process_hop(), and setTimeStretchAndPitchFactor().

◆ d

std::shared_ptr<impl> staffpad::TimeAndPitch::d
private

◆ fftSize

const int staffpad::TimeAndPitch::fftSize
private

◆ modulate_synthesis_hop

constexpr bool staffpad::TimeAndPitch::modulate_synthesis_hop = true
staticconstexprprivate

Definition at line 90 of file TimeAndPitch.h.

Referenced by retrieveAudio(), and setTimeStretchAndPitchFactor().

◆ normalize_window

constexpr bool staffpad::TimeAndPitch::normalize_window = true
staticconstexprprivate

Definition at line 89 of file TimeAndPitch.h.

Referenced by _process_hop(), and retrieveAudio().

◆ overlap

constexpr int staffpad::TimeAndPitch::overlap = 4
staticconstexprprivate

Definition at line 88 of file TimeAndPitch.h.

Referenced by getLatencySamples(), and setTimeStretchAndPitchFactor().


The documentation for this class was generated from the following files: