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 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 632 of file WaveClip.cpp.

640 {
641  bool result = false;
642  const bool reassignment =
644  const size_t windowSizeSetting = settings.WindowSize();
645 
646  sampleCount from;
647 
648  // xx may be for a column that is out of the visible bounds, but only
649  // when we are calculating reassignment contributions that may cross into
650  // the visible area.
651 
652  if (xx < 0)
653  from = sampleCount(
654  where[0].as_double() + xx * (rate / pixelsPerSecond)
655  );
656  else if (xx > (int)len)
657  from = sampleCount(
658  where[len].as_double() + (xx - len) * (rate / pixelsPerSecond)
659  );
660  else
661  from = where[xx];
662 
663  const bool autocorrelation =
665  const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
666  const size_t padding = (windowSizeSetting * (zeroPaddingFactorSetting - 1)) / 2;
667  const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
668  auto nBins = settings.NBins();
669 
670  if (from < 0 || from >= numSamples) {
671  if (xx >= 0 && xx < (int)len) {
672  // Pixel column is out of bounds of the clip! Should not happen.
673  float *const results = &out[nBins * xx];
674  std::fill(results, results + nBins, 0.0f);
675  }
676  }
677  else {
678 
679 
680  // We can avoid copying memory when ComputeSpectrum is used below
681  bool copy = !autocorrelation || (padding > 0) || reassignment;
682  float *useBuffer = 0;
683  float *adj = scratch + padding;
684 
685  {
686  auto myLen = windowSizeSetting;
687  // Take a window of the track centered at this sample.
688  from -= windowSizeSetting >> 1;
689  if (from < 0) {
690  // Near the start of the clip, pad left with zeroes as needed.
691  // from is at least -windowSize / 2
692  for (auto ii = from; ii < 0; ++ii)
693  *adj++ = 0;
694  myLen += from.as_long_long(); // add a negative
695  from = 0;
696  copy = true;
697  }
698 
699  if (from + myLen >= numSamples) {
700  // Near the end of the clip, pad right with zeroes as needed.
701  // newlen is bounded by myLen:
702  auto newlen = ( numSamples - from ).as_size_t();
703  for (decltype(myLen) ii = newlen; ii < myLen; ++ii)
704  adj[ii] = 0;
705  myLen = newlen;
706  copy = true;
707  }
708 
709  if (myLen > 0) {
710  useBuffer = (float*)(waveTrackCache.GetFloats(
711  sampleCount(
712  floor(0.5 + from.as_double() + offset * rate)
713  ),
714  myLen,
715  // Don't throw in this drawing operation
716  false)
717  );
718 
719  if (copy) {
720  if (useBuffer)
721  memcpy(adj, useBuffer, myLen * sizeof(float));
722  else
723  memset(adj, 0, myLen * sizeof(float));
724  }
725  }
726  }
727 
728  if (copy || !useBuffer)
729  useBuffer = scratch;
730 
731  if (autocorrelation) {
732  // not reassignment, xx is surely within bounds.
733  wxASSERT(xx >= 0);
734  float *const results = &out[nBins * xx];
735  // This function does not mutate useBuffer
736  ComputeSpectrum(useBuffer, windowSizeSetting, windowSizeSetting,
737  rate, results,
738  autocorrelation, settings.windowType);
739  }
740  else if (reassignment) {
741  static const double epsilon = 1e-16;
742  const auto hFFT = settings.hFFT.get();
743 
744  float *const scratch2 = scratch + fftLen;
745  std::copy(scratch, scratch2, scratch2);
746 
747  float *const scratch3 = scratch + 2 * fftLen;
748  std::copy(scratch, scratch2, scratch3);
749 
750  {
751  const float *const window = settings.window.get();
752  for (size_t ii = 0; ii < fftLen; ++ii)
753  scratch[ii] *= window[ii];
754  RealFFTf(scratch, hFFT);
755  }
756 
757  {
758  const float *const dWindow = settings.dWindow.get();
759  for (size_t ii = 0; ii < fftLen; ++ii)
760  scratch2[ii] *= dWindow[ii];
761  RealFFTf(scratch2, hFFT);
762  }
763 
764  {
765  const float *const tWindow = settings.tWindow.get();
766  for (size_t ii = 0; ii < fftLen; ++ii)
767  scratch3[ii] *= tWindow[ii];
768  RealFFTf(scratch3, hFFT);
769  }
770 
771  for (size_t ii = 0; ii < hFFT->Points; ++ii) {
772  const int index = hFFT->BitReversed[ii];
773  const float
774  denomRe = scratch[index],
775  denomIm = ii == 0 ? 0 : scratch[index + 1];
776  const double power = denomRe * denomRe + denomIm * denomIm;
777  if (power < epsilon)
778  // Avoid dividing by near-zero below
779  continue;
780 
781  double freqCorrection;
782  {
783  const double multiplier = -(fftLen / (2.0f * M_PI));
784  const float
785  numRe = scratch2[index],
786  numIm = ii == 0 ? 0 : scratch2[index + 1];
787  // Find complex quotient --
788  // Which means, multiply numerator by conjugate of denominator,
789  // then divide by norm squared of denominator --
790  // Then just take its imaginary part.
791  const double
792  quotIm = (-numRe * denomIm + numIm * denomRe) / power;
793  // With appropriate multiplier, that becomes the correction of
794  // the frequency bin.
795  freqCorrection = multiplier * quotIm;
796  }
797 
798  const int bin = (int)((int)ii + freqCorrection + 0.5f);
799  // Must check if correction takes bin out of bounds, above or below!
800  // bin is signed!
801  if (bin >= 0 && bin < (int)hFFT->Points) {
802  double timeCorrection;
803  {
804  const float
805  numRe = scratch3[index],
806  numIm = ii == 0 ? 0 : scratch3[index + 1];
807  // Find another complex quotient --
808  // Then just take its real part.
809  // The result has sample interval as unit.
810  timeCorrection =
811  (numRe * denomRe + numIm * denomIm) / power;
812  }
813 
814  int correctedX = (floor(0.5 + xx + timeCorrection * pixelsPerSecond / rate));
815  if (correctedX >= lowerBoundX && correctedX < upperBoundX)
816  {
817  result = true;
818 
819  // This is non-negative, because bin and correctedX are
820  auto ind = (int)nBins * correctedX + bin;
821 #ifdef _OPENMP
822  // This assignment can race if index reaches into another thread's bins.
823  // The probability of a race very low, so this carries little overhead,
824  // about 5% slower vs allowing it to race.
825  #pragma omp atomic update
826 #endif
827  out[ind] += power;
828  }
829  }
830  }
831  }
832  else {
833  // not reassignment, xx is surely within bounds.
834  wxASSERT(xx >= 0);
835  float *const results = &out[nBins * xx];
836 
837  // Do the FFT. Note that useBuffer is multiplied by the window,
838  // and the window is initialized with leading and trailing zeroes
839  // when there is padding. Therefore we did not need to reinitialize
840  // the part of useBuffer in the padding zones.
841 
842  // This function mutates useBuffer
844  (useBuffer, settings.hFFT.get(), settings.window.get(), fftLen, results);
845  if (!gainFactors.empty()) {
846  // Apply a frequency-dependent gain factor
847  for (size_t ii = 0; ii < nBins; ++ii)
848  results[ii] += gainFactors[ii];
849  }
850  }
851  }
852 
853  return result;
854 }

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 856 of file WaveClip.cpp.

858 {
859  settings.CacheWindows();
860 
861  // len columns, and so many rows, column-major.
862  // Don't take column literally -- this isn't pixel data yet, it's the
863  // raw data to be mapped onto the display.
864  freq.resize(len_ * settings.NBins());
865 
866  // Sample counts corresponding to the columns, and to one past the end.
867  where.resize(len_ + 1);
868 
869  len = len_;
870  algorithm = settings.algorithm;
871  pps = pixelsPerSecond;
872  start = start_;
873  windowType = settings.windowType;
874  windowSize = settings.WindowSize();
875  zeroPaddingFactor = settings.ZeroPaddingFactor();
876  frequencyGain = settings.frequencyGain;
877 }

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 611 of file WaveClip.cpp.

614 {
615  // Make a tolerant comparison of the pps values in this wise:
616  // accumulated difference of times over the number of pixels is less than
617  // a sample period.
618  const double tstep = 1.0 / pixelsPerSecond;
619  const bool ppsMatch =
620  (fabs(tstep - 1.0 / pps) * len < (1.0 / rate));
621 
622  return
623  ppsMatch &&
624  dirty == dirty_ &&
625  windowType == settings.windowType &&
626  windowSize == settings.WindowSize() &&
627  zeroPaddingFactor == settings.ZeroPaddingFactor() &&
628  frequencyGain == settings.frequencyGain &&
629  algorithm == settings.algorithm;
630 }

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 879 of file WaveClip.cpp.

884 {
885  const int &frequencyGainSetting = settings.frequencyGain;
886  const size_t windowSizeSetting = settings.WindowSize();
887  const bool autocorrelation =
889  const bool reassignment =
891 #ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
892  const size_t zeroPaddingFactorSetting = settings.ZeroPaddingFactor();
893 #else
894  const size_t zeroPaddingFactorSetting = 1;
895 #endif
896 
897  // FFT length may be longer than the window of samples that affect results
898  // because of zero padding done for increased frequency resolution
899  const size_t fftLen = windowSizeSetting * zeroPaddingFactorSetting;
900  const auto nBins = settings.NBins();
901 
902  const size_t bufferSize = fftLen;
903  const size_t scratchSize = reassignment ? 3 * bufferSize : bufferSize;
904  std::vector<float> scratch(scratchSize);
905 
906  std::vector<float> gainFactors;
907  if (!autocorrelation)
908  ComputeSpectrogramGainFactors(fftLen, rate, frequencyGainSetting, gainFactors);
909 
910  // Loop over the ranges before and after the copied portion and compute anew.
911  // One of the ranges may be empty.
912  for (int jj = 0; jj < 2; ++jj) {
913  const int lowerBoundX = jj == 0 ? 0 : copyEnd;
914  const int upperBoundX = jj == 0 ? copyBegin : numPixels;
915 
916 #ifdef _OPENMP
917  // Storage for mutable per-thread data.
918  // private clause ensures one copy per thread
919  struct ThreadLocalStorage {
920  ThreadLocalStorage() { }
921  ~ThreadLocalStorage() { }
922 
923  void init(WaveTrackCache &waveTrackCache, size_t scratchSize) {
924  if (!cache) {
925  cache = std::make_unique<WaveTrackCache>(waveTrackCache.GetTrack());
926  scratch.resize(scratchSize);
927  }
928  }
929  std::unique_ptr<WaveTrackCache> cache;
930  std::vector<float> scratch;
931  } tls;
932 
933  #pragma omp parallel for private(tls)
934 #endif
935  for (auto xx = lowerBoundX; xx < upperBoundX; ++xx)
936  {
937 #ifdef _OPENMP
938  tls.init(waveTrackCache, scratchSize);
939  WaveTrackCache& cache = *tls.cache;
940  float* buffer = &tls.scratch[0];
941 #else
942  WaveTrackCache& cache = waveTrackCache;
943  float* buffer = &scratch[0];
944 #endif
946  settings, cache, xx, numSamples,
947  offset, rate, pixelsPerSecond,
948  lowerBoundX, upperBoundX,
949  gainFactors, buffer, &freq[0]);
950  }
951 
952  if (reassignment) {
953  // Need to look beyond the edges of the range to accumulate more
954  // time reassignments.
955  // I'm not sure what's a good stopping criterion?
956  auto xx = lowerBoundX;
957  const double pixelsPerSample = pixelsPerSecond / rate;
958  const int limit = std::min((int)(0.5 + fftLen * pixelsPerSample), 100);
959  for (int ii = 0; ii < limit; ++ii)
960  {
961  const bool result =
963  settings, waveTrackCache, --xx, numSamples,
964  offset, rate, pixelsPerSecond,
965  lowerBoundX, upperBoundX,
966  gainFactors, &scratch[0], &freq[0]);
967  if (!result)
968  break;
969  }
970 
971  xx = upperBoundX;
972  for (int ii = 0; ii < limit; ++ii)
973  {
974  const bool result =
976  settings, waveTrackCache, xx++, numSamples,
977  offset, rate, pixelsPerSecond,
978  lowerBoundX, upperBoundX,
979  gainFactors, &scratch[0], &freq[0]);
980  if (!result)
981  break;
982  }
983 
984  // Now Convert to dB terms. Do this only after accumulating
985  // power values, which may cross columns with the time correction.
986 #ifdef _OPENMP
987  #pragma omp parallel for
988 #endif
989  for (xx = lowerBoundX; xx < upperBoundX; ++xx) {
990  float *const results = &freq[nBins * xx];
991  for (size_t ii = 0; ii < nBins; ++ii) {
992  float &power = results[ii];
993  if (power <= 0)
994  power = -160.0;
995  else
996  power = 10.0*log10f(power);
997  }
998  if (!gainFactors.empty()) {
999  // Apply a frequency-dependent gain factor
1000  for (size_t ii = 0; ii < nBins; ++ii)
1001  results[ii] += gainFactors[ii];
1002  }
1003  }
1004  }
1005  }
1006 }

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 92 of file WaveClip.h.

Referenced by Matches().

◆ freq

std::vector<float> SpecCache::freq

◆ frequencyGain

int SpecCache::frequencyGain

Definition at line 88 of file WaveClip.h.

Referenced by Grow(), and Matches().

◆ 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().

◆ start

double SpecCache::start

Definition at line 84 of file WaveClip.h.

Referenced by Grow().

◆ where

std::vector<sampleCount> SpecCache::where

◆ windowSize

size_t SpecCache::windowSize { 0 }

Definition at line 86 of file WaveClip.h.

Referenced by Grow(), and Matches().

◆ windowType

int SpecCache::windowType

Definition at line 85 of file WaveClip.h.

Referenced by Grow(), and Matches().

◆ zeroPaddingFactor

unsigned SpecCache::zeroPaddingFactor { 0 }

Definition at line 87 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:626
SpecCache::windowType
int windowType
Definition: WaveClip.h:85
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:87
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:2544
SpecCache::dirty
int dirty
Definition: WaveClip.h:92
anonymous_namespace{WaveClip.cpp}::ComputeSpectrogramGainFactors
void ComputeSpectrogramGainFactors(size_t fftLen, double rate, int frequencyGain, std::vector< float > &gainFactors)
Definition: WaveClip.cpp:590
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:644
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
SpectrogramSettings::algPitchEAC
@ algPitchEAC
Definition: SpectrogramSettings.h:159
SpecCache::algorithm
int algorithm
Definition: WaveClip.h:82
SpectrogramSettings::algReassignment
@ algReassignment
Definition: SpectrogramSettings.h:158
SpecCache::len
size_t len
Definition: WaveClip.h:81
SpecCache::freq
std::vector< float > freq
Definition: WaveClip.h:89
M_PI
#define M_PI
Definition: Distortion.cpp:29
SpecCache::where
std::vector< sampleCount > where
Definition: WaveClip.h:90
SpecCache::windowSize
size_t windowSize
Definition: WaveClip.h:86
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:633
SpecCache::start
double start
Definition: WaveClip.h:84
SpecCache::pps
double pps
Definition: WaveClip.h:83
SpecCache::frequencyGain
int frequencyGain
Definition: WaveClip.h:88