예제 #1
0
// Deduce m_FromFrequency from the samples at the beginning of
// the selection. Then set some other params accordingly.
void EffectChangePitch::DeduceFrequencies()
{
   // As a neat trick, attempt to get the frequency of the note at the
   // beginning of the selection.
   SelectedTrackListOfKindIterator iter(Track::Wave, inputTracks());
   WaveTrack *track = (WaveTrack *) iter.First();
   if (track) {
      double rate = track->GetRate();

      // Auto-size window -- high sample rates require larger windowSize.
      // Aim for around 2048 samples at 44.1 kHz (good down to about 100 Hz).
      // To detect single notes, analysis period should be about 0.2 seconds.
      // windowSize must be a power of 2.
      const size_t windowSize =
         // windowSize < 256 too inaccurate
         std::max(256, wxRound(pow(2.0, floor((log(rate / 20.0)/log(2.0)) + 0.5))));

      // we want about 0.2 seconds to catch the first note.
      // number of windows rounded to nearest integer >= 1.
      const unsigned numWindows =
         std::max(1, wxRound((double)(rate / (5.0f * windowSize))));

      double trackStart = track->GetStartTime();
      double t0 = mT0 < trackStart? trackStart: mT0;
      auto start = track->TimeToLongSamples(t0);

      auto analyzeSize = windowSize * numWindows;
      Floats buffer{ analyzeSize };

      Floats freq{ windowSize / 2 };
      Floats freqa{ windowSize / 2, true };

      track->Get((samplePtr) buffer.get(), floatSample, start, analyzeSize);
      for(unsigned i = 0; i < numWindows; i++) {
         ComputeSpectrum(buffer.get() + i * windowSize, windowSize,
                         windowSize, rate, freq.get(), true);
         for(size_t j = 0; j < windowSize / 2; j++)
            freqa[j] += freq[j];
      }
      size_t argmax = 0;
      for(size_t j = 1; j < windowSize / 2; j++)
         if (freqa[j] > freqa[argmax])
            argmax = j;

      auto lag = (windowSize / 2 - 1) - argmax;
      m_dStartFrequency = rate / lag;
   }

   double dFromMIDInote = FreqToMIDInote(m_dStartFrequency);
   double dToMIDInote = dFromMIDInote + m_dSemitonesChange;
   m_nFromPitch = PitchIndex(dFromMIDInote);
   m_nFromOctave = PitchOctave(dFromMIDInote);
   m_nToPitch = PitchIndex(dToMIDInote);
   m_nToOctave = PitchOctave(dToMIDInote);

   m_FromFrequency = m_dStartFrequency;
   Calc_PercentChange();
   Calc_ToFrequency();
}
예제 #2
0
// DeduceFrequencies is Dominic's extremely cool trick (Vaughan sez so!) 
// to set deduce m_FromFrequency from the samples at the beginning of 
// the selection. Then we set some other params accordingly.
void EffectChangePitch::DeduceFrequencies()
{
   // As a neat trick, attempt to get the frequency of the note at the
   // beginning of the selection.
   SelectedTrackListOfKindIterator iter(Track::Wave, mTracks);
   WaveTrack *track = (WaveTrack *) iter.First();
   if (track) {
      const int windowSize = 1024;
      const int analyzeSize = 8192;
      const int numWindows = analyzeSize / windowSize;
      double trackStart = track->GetStartTime();
      double t0 = mT0 < trackStart? trackStart: mT0;
      sampleCount start = track->TimeToLongSamples(t0);
      double rate = track->GetRate();
      float buffer[analyzeSize];
      float freq[windowSize/2];
      float freqa[windowSize/2];
      int i, j, argmax;
      int lag;

      for(j=0; j<windowSize/2; j++)
         freqa[j] = 0;

      track->Get((samplePtr) buffer, floatSample, start, analyzeSize);
      for(i=0; i<numWindows; i++) {
         ComputeSpectrum(buffer+i*windowSize, windowSize,
                         windowSize, rate, freq, true);
         for(j=0; j<windowSize/2; j++)
            freqa[j] += freq[j];
      }
      argmax=0;
      for(j=1; j<windowSize/2; j++)
         if (freqa[j] > freqa[argmax])
            argmax = j;
      lag = (windowSize/2 - 1) - argmax;
      m_FromFrequency = rate / lag;
      m_ToFrequency = (m_FromFrequency * (100.0 + m_PercentChange)) / 100.0;

      // Now we can set the pitch control values. 
      m_FromPitchIndex = PitchIndex(FreqToMIDInoteNumber(m_FromFrequency));
      m_bWantPitchDown = (m_ToFrequency < m_FromFrequency);
      m_ToPitchIndex = PitchIndex(FreqToMIDInoteNumber(m_ToFrequency));
   }
}
예제 #3
0
파일: spec.c 프로젝트: ncareol/ncplot
/* -------------------------------------------------------------------- */
void ToggleMultByFreq(Widget w, XtPointer client, XtPointer call)
{
  /* Only mult by freq or mult by freq^5/3 can be selected.  Grey out the other
   */
  if (multiplyByFreq())
    XtSetSensitive(pmOptButt[3], false);
  else
    XtSetSensitive(pmOptButt[3], true);

  if (multiplyByFreq53())
    XtSetSensitive(pmOptButt[2], false);
  else
    XtSetSensitive(pmOptButt[2], true);


  if (psd[0].display == SPECTRA)
    ComputeSpectrum();
  else
    ComputeCoSpectrum();

}	/* END TOGGLEMULTBYFREQ */
예제 #4
0
bool WaveClip::GetSpectrogram(float *freq, sampleCount *where,
                               int numPixels,
                               double t0, double pixelsPerSecond,
                               bool autocorrelation)
{
   int minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
   int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);
   int range = gPrefs->Read(wxT("/Spectrum/Range"), 80L);
   int gain = gPrefs->Read(wxT("/Spectrum/Gain"), 20L);
   int frequencygain = gPrefs->Read(wxT("/Spectrum/FrequencyGain"), 0L);
   int windowType;
   int windowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256);
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
   int fftSkipPoints = gPrefs->Read(wxT("/Spectrum/FFTSkipPoints"), 0L);
   int fftSkipPoints1 = fftSkipPoints+1;
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
   int half = windowSize/2;
   gPrefs->Read(wxT("/Spectrum/WindowType"), &windowType, 3);

#ifdef EXPERIMENTAL_USE_REALFFTF
   // Update the FFT and window if necessary
   if((mWindowType != windowType) || (mWindowSize != windowSize)
      || (hFFT == NULL) || (mWindow == NULL) || (mWindowSize != hFFT->Points*2) ) {
      mWindowType = windowType;
      mWindowSize = windowSize;
      if(hFFT != NULL)
         EndFFT(hFFT);
      hFFT = InitializeFFT(mWindowSize);
      if(mWindow != NULL) delete[] mWindow;
      // Create the requested window function
      mWindow = new float[mWindowSize];
      int i;
      for(i=0; i<windowSize; i++)
         mWindow[i]=1.0;
      WindowFunc(mWindowType, mWindowSize, mWindow);
      // Scale the window function to give 0dB spectrum for 0dB sine tone
      double ws=0;
      for(i=0; i<windowSize; i++)
         ws += mWindow[i];
      if(ws > 0) {
         ws = 2.0/ws;
         for(i=0; i<windowSize; i++)
            mWindow[i] *= ws;
      }
   }
#endif // EXPERIMENTAL_USE_REALFFTF

   if (mSpecCache &&
       mSpecCache->minFreqOld == minFreq &&
       mSpecCache->maxFreqOld == maxFreq &&
       mSpecCache->rangeOld == range &&
       mSpecCache->gainOld == gain &&
       mSpecCache->windowTypeOld == windowType &&
       mSpecCache->windowSizeOld == windowSize &&
       mSpecCache->frequencyGainOld == frequencygain &&
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
       mSpecCache->fftSkipPointsOld == fftSkipPoints &&
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
       mSpecCache->dirty == mDirty &&
       mSpecCache->start == t0 &&
       mSpecCache->ac == autocorrelation &&
       mSpecCache->len >= numPixels &&
       mSpecCache->pps == pixelsPerSecond) {
      memcpy(freq, mSpecCache->freq, numPixels*half*sizeof(float));
      memcpy(where, mSpecCache->where, (numPixels+1)*sizeof(sampleCount));
      return false;  //hit cache completely
   }

   SpecCache *oldCache = mSpecCache;

   mSpecCache = new SpecCache(numPixels, half, autocorrelation);
   mSpecCache->pps = pixelsPerSecond;
   mSpecCache->start = t0;

   sampleCount x;

   bool *recalc = new bool[mSpecCache->len + 1];

   for (x = 0; x < mSpecCache->len + 1; x++) {
      recalc[x] = true;
      // purposely offset the display 1/2 bin to the left (as compared
      // to waveform display to properly center response of the FFT
      mSpecCache->where[x] =
         (sampleCount)floor((t0*mRate) + (x*mRate/pixelsPerSecond) + 1.);
   }

   // Optimization: if the old cache is good and overlaps
   // with the current one, re-use as much of the cache as
   // possible
   if (oldCache->dirty == mDirty &&
       oldCache->minFreqOld == minFreq &&
       oldCache->maxFreqOld == maxFreq &&
       oldCache->rangeOld == range &&
       oldCache->gainOld == gain &&
       oldCache->windowTypeOld == windowType &&
       oldCache->windowSizeOld == windowSize &&
       oldCache->frequencyGainOld == frequencygain &&
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
       oldCache->fftSkipPointsOld == fftSkipPoints &&
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
       oldCache->pps == pixelsPerSecond &&
       oldCache->ac == autocorrelation &&
       oldCache->where[0] < mSpecCache->where[mSpecCache->len] &&
       oldCache->where[oldCache->len] > mSpecCache->where[0]) {

      for (x = 0; x < mSpecCache->len; x++)
         if (mSpecCache->where[x] >= oldCache->where[0] &&
             mSpecCache->where[x] <= oldCache->where[oldCache->len]) {

            int ox = (int) ((double (oldCache->len) *
                      (mSpecCache->where[x] - oldCache->where[0]))
                       / (oldCache->where[oldCache->len] -
                                             oldCache->where[0]) + 0.5);
            if (ox >= 0 && ox < oldCache->len &&
                mSpecCache->where[x] == oldCache->where[ox]) {

               for (sampleCount i = 0; i < (sampleCount)half; i++)
                  mSpecCache->freq[half * x + i] =
                     oldCache->freq[half * ox + i];

               recalc[x] = false;
            }
         }
   }

#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
   float *buffer = new float[windowSize*fftSkipPoints1];
   mSpecCache->fftSkipPointsOld = fftSkipPoints;
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
   float *buffer = new float[windowSize];
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
   mSpecCache->minFreqOld = minFreq;
   mSpecCache->maxFreqOld = maxFreq;
   mSpecCache->gainOld = gain;
   mSpecCache->rangeOld = range;
   mSpecCache->windowTypeOld = windowType;
   mSpecCache->windowSizeOld = windowSize;
   mSpecCache->frequencyGainOld = frequencygain;

   float *gainfactor = NULL;
   if(frequencygain > 0) {
      // Compute a frequency-dependant gain factor
      // scaled such that 1000 Hz gets a gain of 0dB
      double factor = 0.001*(double)mRate/(double)windowSize;
      gainfactor = new float[half];
      for(x = 0; x < half; x++) {
         gainfactor[x] = frequencygain*log10(factor * x);
      }
   }

   for (x = 0; x < mSpecCache->len; x++)
      if (recalc[x]) {

         sampleCount start = mSpecCache->where[x];
         sampleCount len = windowSize;
         sampleCount i;

         if (start <= 0 || start >= mSequence->GetNumSamples()) {

            for (i = 0; i < (sampleCount)half; i++)
               mSpecCache->freq[half * x + i] = 0;

         }
         else
         {
            float *adj = buffer;
            start -= windowSize >> 1;

            if (start < 0) {
               for (i = start; i < 0; i++)
                  *adj++ = 0;
               len += start;
               start = 0;
            }
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
            if (start + len*fftSkipPoints1 > mSequence->GetNumSamples()) {
               int newlen = (mSequence->GetNumSamples() - start)/fftSkipPoints1;
               for (i = newlen*fftSkipPoints1; i < (sampleCount)len*fftSkipPoints1; i++)
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
            if (start + len > mSequence->GetNumSamples()) {
               int newlen = mSequence->GetNumSamples() - start;
               for (i = newlen; i < (sampleCount)len; i++)
#endif //EXPERIMENTAL_FFT_SKIP_POINTS
                  adj[i] = 0;
               len = newlen;
            }

            if (len > 0)
#ifdef EXPERIMENTAL_FFT_SKIP_POINTS
               mSequence->Get((samplePtr)adj, floatSample, start, len*fftSkipPoints1);
            if (fftSkipPoints) {
               // TODO: (maybe) alternatively change Get to include skipping of points
               int j=0;
               for (int i=0; i < len; i++) {
                  adj[i]=adj[j];
                  j+=fftSkipPoints1;
               }
            }
#else //!EXPERIMENTAL_FFT_SKIP_POINTS
               mSequence->Get((samplePtr)adj, floatSample, start, len);
#endif //EXPERIMENTAL_FFT_SKIP_POINTS

#ifdef EXPERIMENTAL_USE_REALFFTF
            if(autocorrelation) {
               ComputeSpectrum(buffer, windowSize, windowSize,
                               mRate, &mSpecCache->freq[half * x],
                               autocorrelation, windowType);
            } else {
               ComputeSpectrumUsingRealFFTf(buffer, hFFT, mWindow, mWindowSize, &mSpecCache->freq[half * x]);
            }
#else  // EXPERIMENTAL_USE_REALFFTF
           ComputeSpectrum(buffer, windowSize, windowSize,
                           mRate, &mSpecCache->freq[half * x],
                           autocorrelation, windowType);
#endif // EXPERIMENTAL_USE_REALFFTF
           if(gainfactor) {
              // Apply a frequency-dependant gain factor
              for(i=0; i<half; i++)
                 mSpecCache->freq[half * x + i] += gainfactor[i];
           }
         }
      }

   if(gainfactor)
      delete[] gainfactor;
   delete[]buffer;
   delete[]recalc;
   delete oldCache;

   mSpecCache->dirty = mDirty;
   memcpy(freq, mSpecCache->freq, numPixels*half*sizeof(float));
   memcpy(where, mSpecCache->where, (numPixels+1)*sizeof(sampleCount));
   return true;
}

bool WaveClip::GetMinMax(float *min, float *max,
                          double t0, double t1)
{
   *min = float(0.0);
   *max = float(0.0);

   if (t0 > t1)
      return false;

   if (t0 == t1)
      return true;

   sampleCount s0, s1;

   TimeToSamplesClip(t0, &s0);
   TimeToSamplesClip(t1, &s1);

   return mSequence->GetMinMax(s0, s1-s0, min, max);
}
예제 #5
0
// Deduce m_FromFrequency from the samples at the beginning of
// the selection. Then set some other params accordingly.
void EffectChangePitch::DeduceFrequencies()
{
   // As a neat trick, attempt to get the frequency of the note at the
   // beginning of the selection.
   SelectedTrackListOfKindIterator iter(Track::Wave, mTracks);
   WaveTrack *track = (WaveTrack *) iter.First();
   if (track) {
      double rate = track->GetRate();

      // Auto-size window -- high sample rates require larger windowSize.
      // Aim for around 2048 samples at 44.1 kHz (good down to about 100 Hz).
      // To detect single notes, analysis period should be about 0.2 seconds.
      // windowSize must be a power of 2.
      int windowSize = wxRound(pow(2.0, floor((log(rate / 20.0)/log(2.0)) + 0.5)));
      // windowSize < 256 too inaccurate
      windowSize = (windowSize > 256)? windowSize : 256;

      // we want about 0.2 seconds to catch the first note.
      // number of windows rounded to nearest integer >= 1.
      int numWindows = wxRound((double)(rate / (5.0f * windowSize)));
      numWindows = (numWindows > 0)? numWindows : 1;

      double trackStart = track->GetStartTime();
      double t0 = mT0 < trackStart? trackStart: mT0;
      sampleCount start = track->TimeToLongSamples(t0);

      int analyzeSize = windowSize * numWindows;
      float * buffer;
      buffer = new float[analyzeSize];

      float * freq;
      freq = new float[windowSize/2];

      float * freqa;
      freqa = new float[windowSize/2];

      int i, j, argmax;
      int lag;

      for(j=0; j<windowSize/2; j++)
         freqa[j] = 0;

      track->Get((samplePtr) buffer, floatSample, start, analyzeSize);
      for(i=0; i<numWindows; i++) {
         ComputeSpectrum(buffer+i*windowSize, windowSize,
                         windowSize, rate, freq, true);
         for(j=0; j<windowSize/2; j++)
            freqa[j] += freq[j];
      }
      argmax=0;
      for(j=1; j<windowSize/2; j++)
         if (freqa[j] > freqa[argmax])
            argmax = j;

      delete [] freq;
      delete [] freqa;
      delete [] buffer;

      lag = (windowSize/2 - 1) - argmax;
      m_dStartFrequency = rate / lag;
   }

   double dFromMIDInote = FreqToMIDInote(m_dStartFrequency);
   double dToMIDInote = dFromMIDInote + m_dSemitonesChange;
   m_nFromPitch = PitchIndex(dFromMIDInote);
   m_nFromOctave = PitchOctave(dFromMIDInote);
   m_nToPitch = PitchIndex(dToMIDInote);
   m_nToOctave = PitchOctave(dToMIDInote);

   m_FromFrequency = m_dStartFrequency;
   Calc_PercentChange();
   Calc_ToFrequency();
}
예제 #6
0
파일: spec.c 프로젝트: ncareol/ncplot
/* -------------------------------------------------------------------- */
void SpecWinUp(Widget w, XtPointer client, XtPointer call)
{
  int	i, nSets;
  bool	saveState = Freeze;
  XmToggleButtonCallbackStruct *cb = (XmToggleButtonCallbackStruct *)call;

  static bool firstTime = true;

  /* If this is 'unset' from one of the ToggleButtons, then bail.
   */
  if (cb && cb->reason == XmCR_VALUE_CHANGED && cb->set == false)
    return;

  if (NumberDataSets < 1)
    return;

  if ((long)client > 1 && NumberDataSets < 2)
    {
    HandleError("Co-PSD requires two data sets.", Interactive, IRET);
    return;
    }

  WaitCursor(MainWindow);
  WaitCursor(ControlWindow);

  if (client)
    psd[0].display = (long)client;

  if (firstTime)
    {
    CreateSpectrumWindow();
    initPlotGC(&specPlot);
    }
  else
    WaitCursor(SpectrumWindow);

  for (i = 0; i < 6; ++i)
    if (i == psd[0].display - 1)
      XmToggleButtonSetState(typeButts[i], true, false);
    else
      XmToggleButtonSetState(typeButts[i], false, false);

  Freeze = true;
  psd[0].frequency = dataSet[0].nPoints / NumberSeconds;
  psd[0].freqPerBin = (double)psd[0].frequency / (psd[0].M << 1);

  switch (psd[0].display)
    {
    case SPECTRA:
      XtSetSensitive(pmOptButt[1], true);
      XtSetSensitive(pmOptButt[2], true);
      XtSetSensitive(pmOptButt[3], true);

      nSets = std::min(NumberDataSets, MAX_PSD);

      for (i = 1; i < nSets; ++i)
        {
        psd[i].frequency = dataSet[i].nPoints / NumberSeconds;
        psd[i].freqPerBin = (double)psd[i].frequency / (psd[i].M << 1);
        }

      ComputeSpectrum();
      break;

    case COSPECTRA:
    case QUADRATURE:
    case RATIO:
      XtSetSensitive(pmOptButt[1], false);
      XtSetSensitive(pmOptButt[2], true);
      XtSetSensitive(pmOptButt[3], true);
      ComputeCoSpectrum();
      break;

    case COHERENCE:
    case PHASE:
      XtSetSensitive(pmOptButt[1], false);
      XtSetSensitive(pmOptButt[2], false);
      XtSetSensitive(pmOptButt[3], false);
      ComputeCoSpectrum();
      break;

    default:
      fprintf(stderr, "SpecWinUp: Invalid spectral display.\n");
      return;
    }

  Freeze = saveState;

  specPlot.windowOpen = true;
  XtManageChild(SpectrumWindow);
  XtPopup(XtParent(SpectrumWindow), XtGrabNone);

  if (firstTime)
    {
    WaitCursor(SpectrumWindow);
    ResizeSpecWindow(NULL, NULL, NULL);
    setDefaults();

    XtAddCallback(specPlot.canvas, XmNexposeCallback,
				(XtCallbackProc)PlotSpectrum, NULL);
    XtAddCallback(specPlot.canvas, XmNresizeCallback,
				(XtCallbackProc)ResizeSpecWindow, NULL);
    XtAddCallback(specPlot.canvas, XmNresizeCallback,
				(XtCallbackProc)PlotSpectrum, NULL);

    firstTime = false;
    }

  PlotSpectrum(NULL, NULL, NULL);

  if (AsciiWinOpen)
    SetASCIIdata(NULL, NULL, NULL);

  PointerCursor(MainWindow);
  PointerCursor(ControlWindow);
  PointerCursor(SpectrumWindow);

}   /* END SPECWINUP */
예제 #7
0
bool WaveTrack::GetSpectrogram(float *freq, sampleCount *where,
                               int numPixels, int height,
                               double t0, double pixelsPerSecond,
                               bool autocorrelation)
{
   int maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);;
   if (mSpecCache &&
       mSpecCache->maxFreqOld == maxFreq &&
       mSpecCache->dirty == mDirty &&
       mSpecCache->start == t0 &&
       mSpecCache->ac == autocorrelation &&
       mSpecCache->height == height &&
       mSpecCache->len >= numPixels &&
       mSpecCache->pps == pixelsPerSecond) {
      memcpy(freq, mSpecCache->freq, numPixels*height*sizeof(float));
      memcpy(where, mSpecCache->where, (numPixels+1)*sizeof(sampleCount));
      return true;
   }

   SpecCache *oldCache = mSpecCache;

   mSpecCache = new SpecCache(numPixels, height, autocorrelation);
   mSpecCache->pps = pixelsPerSecond;
   mSpecCache->start = t0;

   sampleCount x;

   bool *recalc = new bool[mSpecCache->len + 1];

   for (x = 0; x < mSpecCache->len + 1; x++) {
      recalc[x] = true;
      mSpecCache->where[x] =
         (sampleCount)floor((t0*mRate) + (x*mRate/pixelsPerSecond) + 0.5);
   }

   // Optimization: if the old cache is good and overlaps
   // with the current one, re-use as much of the cache as
   // possible
   if (oldCache->dirty == GetDirty() &&
       oldCache->maxFreqOld == maxFreq &&
       oldCache->pps == pixelsPerSecond &&
       oldCache->height == height &&
       oldCache->ac == autocorrelation &&
       oldCache->where[0] < mSpecCache->where[mSpecCache->len] &&
       oldCache->where[oldCache->len] > mSpecCache->where[0]) {

      for (x = 0; x < mSpecCache->len; x++)
         if (mSpecCache->where[x] >= oldCache->where[0] &&
             mSpecCache->where[x] <= oldCache->where[oldCache->len - 1]) {

            int ox = (int) ((double (oldCache->len) *
                      (mSpecCache->where[x] - oldCache->where[0]))
                       / (oldCache->where[oldCache->len] -
                                             oldCache->where[0]) + 0.5);
            if (ox >= 0 && ox <= oldCache->len &&
                mSpecCache->where[x] == oldCache->where[ox]) {

               for (sampleCount i = 0; i < (sampleCount)height; i++)
                  mSpecCache->freq[height * x + i] =
                     oldCache->freq[height * ox + i];

               recalc[x] = false;
            }
         }
   }

   int defaultMaxFreq = 8000;
   int defaultFFTSize = 256;
   #if (AUDACITY_BRANDING == BRAND_THINKLABS)
      // Thinklabs has lower default for Spectrum MaxFreq & bigger FFTSize than standard Audacity
      defaultMaxFreq = 1000;
      defaultFFTSize = 4096;
   #elif (AUDACITY_BRANDING == BRAND_AUDIOTOUCH)
      defaultMaxFreq = 3000;
      defaultFFTSize = 4096;
   #endif
   int windowSize = gPrefs->Read("/Spectrum/FFTSize", defaultFFTSize);
   float *buffer = new float[windowSize];
   mSpecCache->maxFreqOld = maxFreq;

   for (x = 0; x < mSpecCache->len; x++)
      if (recalc[x]) {

         sampleCount start = mSpecCache->where[x];
         sampleCount len = windowSize;

         sampleCount i;

         if (start >= mSequence->GetNumSamples()) {
            for (i = 0; i < (sampleCount)height; i++)
               mSpecCache->freq[height * x + i] = 0;

         } else {

            if (start + len > mSequence->GetNumSamples()) {
               len = mSequence->GetNumSamples() - start;
               for (i = len; i < (sampleCount)windowSize; i++)
                  buffer[i] = 0;
            }

            mSequence->Get((samplePtr)buffer, floatSample,
                           start, len);

            ComputeSpectrum(buffer, windowSize, height,
                            maxFreq, windowSize,
                            mRate, &mSpecCache->freq[height * x],
                            autocorrelation);
         }
      }

   delete[]buffer;
   delete[]recalc;
   delete oldCache;

   mSpecCache->dirty = GetDirty();
   memcpy(freq, mSpecCache->freq, numPixels*height*sizeof(float));
   memcpy(where, mSpecCache->where, (numPixels+1)*sizeof(sampleCount));

   return true;
}