Audacity  3.0.3
Public Member Functions | Public Attributes | List of all members
SpecCache Class Reference

#include <WaveClip.h>

Collaboration diagram for SpecCache:
[legend]

Public Member Functions

 SpecCache ()
 
 ~SpecCache ()
 
bool Matches (int dirty_, double pixelsPerSecond, const SpectrogramSettings &settings, double rate) const
 
bool CalculateOneSpectrum (const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache, const int xx, sampleCount numSamples, double offset, double rate, double pixelsPerSecond, int lowerBoundX, int upperBoundX, const std::vector< float > &gainFactors, float *__restrict scratch, float *__restrict out) const
 
void Grow (size_t len_, const SpectrogramSettings &settings, double pixelsPerSecond, double start_)
 
void Populate (const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache, int copyBegin, int copyEnd, size_t numPixels, sampleCount numSamples, double offset, double rate, double pixelsPerSecond)
 

Public Attributes

size_t len { 0 }
 
int algorithm
 
double pps
 
double leftTrim { .0 }
 
double rightTrim { .0 }
 
double start
 
int windowType
 
size_t windowSize { 0 }
 
unsigned zeroPaddingFactor { 0 }
 
int frequencyGain
 
std::vector< float > freq
 
std::vector< sampleCountwhere
 
int dirty
 

Detailed Description

Definition at line 38 of file WaveClip.h.

Constructor & Destructor Documentation

◆ SpecCache()

SpecCache::SpecCache ( )
inline

Definition at line 42 of file WaveClip.h.

43  : algorithm(-1)
44  , pps(-1.0)
45  , start(-1.0)
46  , windowType(-1)
47  , frequencyGain(-1)
48  , dirty(-1)
49  {
50  }

◆ ~SpecCache()

SpecCache::~SpecCache ( )
inline

Definition at line 52 of file WaveClip.h.

53  {
54  }

Member Function Documentation

◆ CalculateOneSpectrum()

bool SpecCache::CalculateOneSpectrum ( const SpectrogramSettings settings,
WaveTrackCache waveTrackCache,
const int  xx,
sampleCount  numSamples,
double  offset,
double  rate,
double  pixelsPerSecond,
int  lowerBoundX,
int  upperBoundX,
const std::vector< float > &  gainFactors,
float *__restrict  scratch,
float *__restrict  out 
) const

Definition at line 565 of file WaveClip.cpp.

573 {
574  bool result = false;
575  const bool reassignment =
577  const size_t windowSizeSetting = settings.WindowSize();
578 
579  sampleCount from;
580 
581  // xx may be for a column that is out of the visible bounds, but only
582  // when we are calculating reassignment contributions that may cross into
583  // the visible area.
584 
585  if (xx < 0)
586  from = sampleCount(
587  where[0].as_double() + xx * (rate / pixelsPerSecond)
588  );
589  else if (xx > (int)len)
590  from = sampleCount(
591  where[len].as_double() + (xx - len) * (rate / pixelsPerSecond)
592  );
593  else
594  from = where[xx];
595 
596  const bool autocorrelation =
598  const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
599  const size_t padding = (windowSizeSetting * (zeroPaddingFactorSetting - 1)) / 2;
600  const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
601  auto nBins = settings.NBins();
602 
603  if (from < 0 || from >= numSamples) {
604  if (xx >= 0 && xx < (int)len) {
605  // Pixel column is out of bounds of the clip! Should not happen.
606  float *const results = &out[nBins * xx];
607  std::fill(results, results + nBins, 0.0f);
608  }
609  }
610  else {
611 
612 
613  // We can avoid copying memory when ComputeSpectrum is used below
614  bool copy = !autocorrelation || (padding > 0) || reassignment;
615  float *useBuffer = 0;
616  float *adj = scratch + padding;
617 
618  {
619  auto myLen = windowSizeSetting;
620  // Take a window of the track centered at this sample.
621  from -= windowSizeSetting >> 1;
622  if (from < 0) {
623  // Near the start of the clip, pad left with zeroes as needed.
624  // from is at least -windowSize / 2
625  for (auto ii = from; ii < 0; ++ii)
626  *adj++ = 0;
627  myLen += from.as_long_long(); // add a negative
628  from = 0;
629  copy = true;
630  }
631 
632  if (from + myLen >= numSamples) {
633  // Near the end of the clip, pad right with zeroes as needed.
634  // newlen is bounded by myLen:
635  auto newlen = ( numSamples - from ).as_size_t();
636  for (decltype(myLen) ii = newlen; ii < myLen; ++ii)
637  adj[ii] = 0;
638  myLen = newlen;
639  copy = true;
640  }
641 
642  if (myLen > 0) {
643  useBuffer = (float*)(waveTrackCache.GetFloats(
644  sampleCount(
645  floor(0.5 + from.as_double() + offset * rate)
646  ),
647  myLen,
648  // Don't throw in this drawing operation
649  false)
650  );
651 
652  if (copy) {
653  if (useBuffer)
654  memcpy(adj, useBuffer, myLen * sizeof(float));
655  else
656  memset(adj, 0, myLen * sizeof(float));
657  }
658  }
659  }
660 
661  if (copy || !useBuffer)
662  useBuffer = scratch;
663 
664  if (autocorrelation) {
665  // not reassignment, xx is surely within bounds.
666  wxASSERT(xx >= 0);
667  float *const results = &out[nBins * xx];
668  // This function does not mutate useBuffer
669  ComputeSpectrum(useBuffer, windowSizeSetting, windowSizeSetting,
670  rate, results,
671  autocorrelation, settings.windowType);
672  }
673  else if (reassignment) {
674  static const double epsilon = 1e-16;
675  const auto hFFT = settings.hFFT.get();
676 
677  float *const scratch2 = scratch + fftLen;
678  std::copy(scratch, scratch2, scratch2);
679 
680  float *const scratch3 = scratch + 2 * fftLen;
681  std::copy(scratch, scratch2, scratch3);
682 
683  {
684  const float *const window = settings.window.get();
685  for (size_t ii = 0; ii < fftLen; ++ii)
686  scratch[ii] *= window[ii];
687  RealFFTf(scratch, hFFT);
688  }
689 
690  {
691  const float *const dWindow = settings.dWindow.get();
692  for (size_t ii = 0; ii < fftLen; ++ii)
693  scratch2[ii] *= dWindow[ii];
694  RealFFTf(scratch2, hFFT);
695  }
696 
697  {
698  const float *const tWindow = settings.tWindow.get();
699  for (size_t ii = 0; ii < fftLen; ++ii)
700  scratch3[ii] *= tWindow[ii];
701  RealFFTf(scratch3, hFFT);
702  }
703 
704  for (size_t ii = 0; ii < hFFT->Points; ++ii) {
705  const int index = hFFT->BitReversed[ii];
706  const float
707  denomRe = scratch[index],
708  denomIm = ii == 0 ? 0 : scratch[index + 1];
709  const double power = denomRe * denomRe + denomIm * denomIm;
710  if (power < epsilon)
711  // Avoid dividing by near-zero below
712  continue;
713 
714  double freqCorrection;
715  {
716  const double multiplier = -(fftLen / (2.0f * M_PI));
717  const float
718  numRe = scratch2[index],
719  numIm = ii == 0 ? 0 : scratch2[index + 1];
720  // Find complex quotient --
721  // Which means, multiply numerator by conjugate of denominator,
722  // then divide by norm squared of denominator --
723  // Then just take its imaginary part.
724  const double
725  quotIm = (-numRe * denomIm + numIm * denomRe) / power;
726  // With appropriate multiplier, that becomes the correction of
727  // the frequency bin.
728  freqCorrection = multiplier * quotIm;
729  }
730 
731  const int bin = (int)((int)ii + freqCorrection + 0.5f);
732  // Must check if correction takes bin out of bounds, above or below!
733  // bin is signed!
734  if (bin >= 0 && bin < (int)hFFT->Points) {
735  double timeCorrection;
736  {
737  const float
738  numRe = scratch3[index],
739  numIm = ii == 0 ? 0 : scratch3[index + 1];
740  // Find another complex quotient --
741  // Then just take its real part.
742  // The result has sample interval as unit.
743  timeCorrection =
744  (numRe * denomRe + numIm * denomIm) / power;
745  }
746 
747  int correctedX = (floor(0.5 + xx + timeCorrection * pixelsPerSecond / rate));
748  if (correctedX >= lowerBoundX && correctedX < upperBoundX)
749  {
750  result = true;
751 
752  // This is non-negative, because bin and correctedX are
753  auto ind = (int)nBins * correctedX + bin;
754 #ifdef _OPENMP
755  // This assignment can race if index reaches into another thread's bins.
756  // The probability of a race very low, so this carries little overhead,
757  // about 5% slower vs allowing it to race.
758  #pragma omp atomic update
759 #endif
760  out[ind] += power;
761  }
762  }
763  }
764  }
765  else {
766  // not reassignment, xx is surely within bounds.
767  wxASSERT(xx >= 0);
768  float *const results = &out[nBins * xx];
769 
770  // Do the FFT. Note that useBuffer is multiplied by the window,
771  // and the window is initialized with leading and trailing zeroes
772  // when there is padding. Therefore we did not need to reinitialize
773  // the part of useBuffer in the padding zones.
774 
775  // This function mutates useBuffer
777  (useBuffer, settings.hFFT.get(), settings.window.get(), fftLen, results);
778  if (!gainFactors.empty()) {
779  // Apply a frequency-dependent gain factor
780  for (size_t ii = 0; ii < nBins; ++ii)
781  results[ii] += gainFactors[ii];
782  }
783  }
784  }
785 
786  return result;
787 }

References SpectrogramSettings::algPitchEAC, SpectrogramSettings::algReassignment, sampleCount::as_double(), sampleCount::as_long_long(), ComputeSpectrum(), ComputeSpectrumUsingRealFFTf(), WaveTrackCache::GetFloats(), len, M_PI, RealFFTf(), settings(), and where.

Referenced by Populate().

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

◆ Grow()

void SpecCache::Grow ( size_t  len_,
const SpectrogramSettings settings,
double  pixelsPerSecond,
double  start_ 
)

Definition at line 789 of file WaveClip.cpp.

791 {
792  settings.CacheWindows();
793 
794  // len columns, and so many rows, column-major.
795  // Don't take column literally -- this isn't pixel data yet, it's the
796  // raw data to be mapped onto the display.
797  freq.resize(len_ * settings.NBins());
798 
799  // Sample counts corresponding to the columns, and to one past the end.
800  where.resize(len_ + 1);
801 
802  len = len_;
803  algorithm = settings.algorithm;
804  pps = pixelsPerSecond;
805  start = start_;
806  windowType = settings.windowType;
807  windowSize = settings.WindowSize();
808  zeroPaddingFactor = settings.ZeroPaddingFactor();
809  frequencyGain = settings.frequencyGain;
810 }

References algorithm, freq, frequencyGain, len, pps, settings(), start, where, windowSize, windowType, and zeroPaddingFactor.

Referenced by anonymous_namespace{SpectrumView.cpp}::DrawClipSpectrum().

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

◆ Matches()

bool SpecCache::Matches ( int  dirty_,
double  pixelsPerSecond,
const SpectrogramSettings settings,
double  rate 
) const

Definition at line 544 of file WaveClip.cpp.

547 {
548  // Make a tolerant comparison of the pps values in this wise:
549  // accumulated difference of times over the number of pixels is less than
550  // a sample period.
551  const double tstep = 1.0 / pixelsPerSecond;
552  const bool ppsMatch =
553  (fabs(tstep - 1.0 / pps) * len < (1.0 / rate));
554 
555  return
556  ppsMatch &&
557  dirty == dirty_ &&
558  windowType == settings.windowType &&
559  windowSize == settings.WindowSize() &&
560  zeroPaddingFactor == settings.ZeroPaddingFactor() &&
561  frequencyGain == settings.frequencyGain &&
562  algorithm == settings.algorithm;
563 }

References algorithm, dirty, frequencyGain, len, pps, settings(), windowSize, windowType, and zeroPaddingFactor.

Here is the call graph for this function:

◆ Populate()

void SpecCache::Populate ( const SpectrogramSettings settings,
WaveTrackCache waveTrackCache,
int  copyBegin,
int  copyEnd,
size_t  numPixels,
sampleCount  numSamples,
double  offset,
double  rate,
double  pixelsPerSecond 
)

Definition at line 812 of file WaveClip.cpp.

817 {
818  const int &frequencyGainSetting = settings.frequencyGain;
819  const size_t windowSizeSetting = settings.WindowSize();
820  const bool autocorrelation =
822  const bool reassignment =
824  const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
825 
826  // FFT length may be longer than the window of samples that affect results
827  // because of zero padding done for increased frequency resolution
828  const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
829  const auto nBins = settings.NBins();
830 
831  const size_t bufferSize = fftLen;
832  const size_t scratchSize = reassignment ? 3 * bufferSize : bufferSize;
833  std::vector<float> scratch(scratchSize);
834 
835  std::vector<float> gainFactors;
836  if (!autocorrelation)
837  ComputeSpectrogramGainFactors(fftLen, rate, frequencyGainSetting, gainFactors);
838 
839  // Loop over the ranges before and after the copied portion and compute anew.
840  // One of the ranges may be empty.
841  for (int jj = 0; jj < 2; ++jj) {
842  const int lowerBoundX = jj == 0 ? 0 : copyEnd;
843  const int upperBoundX = jj == 0 ? copyBegin : numPixels;
844 
845 #ifdef _OPENMP
846  // Storage for mutable per-thread data.
847  // private clause ensures one copy per thread
848  struct ThreadLocalStorage {
849  ThreadLocalStorage() { }
850  ~ThreadLocalStorage() { }
851 
852  void init(WaveTrackCache &waveTrackCache, size_t scratchSize) {
853  if (!cache) {
854  cache = std::make_unique<WaveTrackCache>(waveTrackCache.GetTrack());
855  scratch.resize(scratchSize);
856  }
857  }
858  std::unique_ptr<WaveTrackCache> cache;
859  std::vector<float> scratch;
860  } tls;
861 
862  #pragma omp parallel for private(tls)
863 #endif
864  for (auto xx = lowerBoundX; xx < upperBoundX; ++xx)
865  {
866 #ifdef _OPENMP
867  tls.init(waveTrackCache, scratchSize);
868  WaveTrackCache& cache = *tls.cache;
869  float* buffer = &tls.scratch[0];
870 #else
871  WaveTrackCache& cache = waveTrackCache;
872  float* buffer = &scratch[0];
873 #endif
875  settings, cache, xx, numSamples,
876  offset, rate, pixelsPerSecond,
877  lowerBoundX, upperBoundX,
878  gainFactors, buffer, &freq[0]);
879  }
880 
881  if (reassignment) {
882  // Need to look beyond the edges of the range to accumulate more
883  // time reassignments.
884  // I'm not sure what's a good stopping criterion?
885  auto xx = lowerBoundX;
886  const double pixelsPerSample = pixelsPerSecond / rate;
887  const int limit = std::min((int)(0.5 + fftLen * pixelsPerSample), 100);
888  for (int ii = 0; ii < limit; ++ii)
889  {
890  const bool result =
892  settings, waveTrackCache, --xx, numSamples,
893  offset, rate, pixelsPerSecond,
894  lowerBoundX, upperBoundX,
895  gainFactors, &scratch[0], &freq[0]);
896  if (!result)
897  break;
898  }
899 
900  xx = upperBoundX;
901  for (int ii = 0; ii < limit; ++ii)
902  {
903  const bool result =
905  settings, waveTrackCache, xx++, numSamples,
906  offset, rate, pixelsPerSecond,
907  lowerBoundX, upperBoundX,
908  gainFactors, &scratch[0], &freq[0]);
909  if (!result)
910  break;
911  }
912 
913  // Now Convert to dB terms. Do this only after accumulating
914  // power values, which may cross columns with the time correction.
915 #ifdef _OPENMP
916  #pragma omp parallel for
917 #endif
918  for (xx = lowerBoundX; xx < upperBoundX; ++xx) {
919  float *const results = &freq[nBins * xx];
920  for (size_t ii = 0; ii < nBins; ++ii) {
921  float &power = results[ii];
922  if (power <= 0)
923  power = -160.0;
924  else
925  power = 10.0*log10f(power);
926  }
927  if (!gainFactors.empty()) {
928  // Apply a frequency-dependent gain factor
929  for (size_t ii = 0; ii < nBins; ++ii)
930  results[ii] += gainFactors[ii];
931  }
932  }
933  }
934  }
935 }

References SpectrogramSettings::algPitchEAC, SpectrogramSettings::algReassignment, CalculateOneSpectrum(), anonymous_namespace{WaveClip.cpp}::ComputeSpectrogramGainFactors(), freq, WaveTrackCache::GetTrack(), min(), and settings().

Referenced by anonymous_namespace{SpectrumView.cpp}::DrawClipSpectrum().

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

Member Data Documentation

◆ algorithm

int SpecCache::algorithm

Definition at line 82 of file WaveClip.h.

Referenced by Grow(), and Matches().

◆ dirty

int SpecCache::dirty

Definition at line 94 of file WaveClip.h.

Referenced by Matches().

◆ freq

std::vector<float> SpecCache::freq

◆ frequencyGain

int SpecCache::frequencyGain

Definition at line 90 of file WaveClip.h.

Referenced by Grow(), and Matches().

◆ leftTrim

double SpecCache::leftTrim { .0 }

Definition at line 84 of file WaveClip.h.

◆ len

size_t SpecCache::len { 0 }

Definition at line 81 of file WaveClip.h.

Referenced by CalculateOneSpectrum(), Grow(), and Matches().

◆ pps

double SpecCache::pps

Definition at line 83 of file WaveClip.h.

Referenced by Grow(), and Matches().

◆ rightTrim

double SpecCache::rightTrim { .0 }

Definition at line 85 of file WaveClip.h.

◆ start

double SpecCache::start

Definition at line 86 of file WaveClip.h.

Referenced by Grow().

◆ where

std::vector<sampleCount> SpecCache::where

◆ windowSize

size_t SpecCache::windowSize { 0 }

Definition at line 88 of file WaveClip.h.

Referenced by Grow(), and Matches().

◆ windowType

int SpecCache::windowType

Definition at line 87 of file WaveClip.h.

Referenced by Grow(), and Matches().

◆ zeroPaddingFactor

unsigned SpecCache::zeroPaddingFactor { 0 }

Definition at line 89 of file WaveClip.h.

Referenced by Grow(), and Matches().


The documentation for this class was generated from the following files:
WaveTrackCache
A short-lived object, during whose lifetime, the contents of the WaveTrack are assumed not to change.
Definition: WaveTrack.h:636
SpecCache::windowType
int windowType
Definition: WaveClip.h:87
ComputeSpectrum
bool ComputeSpectrum(const float *data, size_t width, size_t windowSize, double WXUNUSED(rate), float *output, bool autocorrelation, int windowFunc)
Definition: Spectrum.cpp:22
SpecCache::zeroPaddingFactor
unsigned zeroPaddingFactor
Definition: WaveClip.h:89
RealFFTf
void RealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:162
ComputeSpectrumUsingRealFFTf
static void ComputeSpectrumUsingRealFFTf(float *__restrict buffer, const FFTParam *hFFT, const float *__restrict window, size_t len, float *__restrict out)
Definition: WaveClip.cpp:93
WaveTrackCache::GetFloats
const float * GetFloats(sampleCount start, size_t len, bool mayThrow)
Retrieve samples as floats from the track or from the memory cache.
Definition: WaveTrack.cpp:2635
SpecCache::dirty
int dirty
Definition: WaveClip.h:94
anonymous_namespace{WaveClip.cpp}::ComputeSpectrogramGainFactors
void ComputeSpectrogramGainFactors(size_t fftLen, double rate, int frequencyGain, std::vector< float > &gainFactors)
Definition: WaveClip.cpp:523
sampleCount::as_double
double as_double() const
Definition: SampleCount.h:45
sampleCount::as_long_long
long long as_long_long() const
Definition: SampleCount.h:47
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
WaveTrackCache::GetTrack
const std::shared_ptr< const WaveTrack > & GetTrack() const
Definition: WaveTrack.h:654
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
SpectrogramSettings::algPitchEAC
@ algPitchEAC
Definition: SpectrogramSettings.h:157
SpecCache::algorithm
int algorithm
Definition: WaveClip.h:82
SpectrogramSettings::algReassignment
@ algReassignment
Definition: SpectrogramSettings.h:156
SpecCache::len
size_t len
Definition: WaveClip.h:81
SpecCache::freq
std::vector< float > freq
Definition: WaveClip.h:91
M_PI
#define M_PI
Definition: Distortion.cpp:29
SpecCache::where
std::vector< sampleCount > where
Definition: WaveClip.h:92
SpecCache::windowSize
size_t windowSize
Definition: WaveClip.h:88
settings
static Settings & settings()
Definition: TrackInfo.cpp:86
SpecCache::CalculateOneSpectrum
bool CalculateOneSpectrum(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache, const int xx, sampleCount numSamples, double offset, double rate, double pixelsPerSecond, int lowerBoundX, int upperBoundX, const std::vector< float > &gainFactors, float *__restrict scratch, float *__restrict out) const
Definition: WaveClip.cpp:566
SpecCache::start
double start
Definition: WaveClip.h:86
SpecCache::pps
double pps
Definition: WaveClip.h:83
SpecCache::frequencyGain
int frequencyGain
Definition: WaveClip.h:90