// 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(); }
// 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)); } }
/* -------------------------------------------------------------------- */ 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 */
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); }
// 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(); }
/* -------------------------------------------------------------------- */ 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 */
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; }