void EffectChangePitch::OnText_FromFrequency(wxCommandEvent & WXUNUSED(evt)) { if (m_bLoopDetect) return; // Empty string causes unpredictable results with ToDouble() and later calculations. // Non-positive frequency makes no sense, but user might still be editing, // so it's not an error, but we do not want to update the values/controls. if (!m_pTextCtrl_FromFrequency->GetValidator()->TransferFromWindow()) { EnableApply(false); return; } double newFromMIDInote = FreqToMIDInote(m_FromFrequency); m_nFromPitch = PitchIndex(newFromMIDInote); m_nFromOctave = PitchOctave(newFromMIDInote); Calc_ToPitch(); Calc_ToFrequency(); Calc_ToOctave(); // Call after Calc_ToFrequency(). m_bLoopDetect = true; { Update_Choice_FromPitch(); Update_Spin_FromOctave(); Update_Choice_ToPitch(); Update_Spin_ToOctave(); Update_Text_ToFrequency(); } m_bLoopDetect = false; // Success. Make sure OK and Preview are enabled, in case we disabled above during editing. EnableApply(true); }
// 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(); }
void FreqWindow::PlotPaint(wxPaintEvent & event) { wxPaintDC dc( (wxWindow *) event.GetEventObject() ); dc.DrawBitmap( *mBitmap, 0, 0, true ); // Fix for Bug 1226 "Plot Spectrum freezes... if insufficient samples selected" if (!mData || mDataLen < mWindowSize) return; dc.SetFont(mFreqFont); wxRect r = mPlotRect; int width = r.width - 2; float xMin, xMax, xRatio, xStep; if (mAlg == SpectrumAnalyst::Spectrum) { xMin = mRate / mWindowSize; xMax = mRate / 2; xRatio = xMax / xMin; if (mLogAxis) xStep = pow(2.0f, (log(xRatio) / log(2.0f)) / width); else xStep = (xMax - xMin) / width; } else { xMin = 0; xMax = mAnalyst->GetProcessedSize() / mRate; xStep = (xMax - xMin) / width; } float xPos = xMin; // Find the peak nearest the cursor and plot it if ( r.Contains(mMouseX, mMouseY) & (mMouseX!=0) & (mMouseX!=r.width-1) ) { if (mLogAxis) xPos = xMin * pow(xStep, mMouseX - (r.x + 1)); else xPos = xMin + xStep * (mMouseX - (r.x + 1)); float bestValue = 0; float bestpeak = mAnalyst->FindPeak(xPos, &bestValue); int px; if (mLogAxis) px = (int)(log(bestpeak / xMin) / log(xStep)); else px = (int)((bestpeak - xMin) * width / (xMax - xMin)); dc.SetPen(wxPen(wxColour(160,160,160), 1, wxSOLID)); AColor::Line(dc, r.x + 1 + px, r.y, r.x + 1 + px, r.y + r.height); // print out info about the cursor location float value; if (mLogAxis) { xPos = xMin * pow(xStep, mMouseX - (r.x + 1)); value = mAnalyst->GetProcessedValue(xPos, xPos * xStep); } else { xPos = xMin + xStep * (mMouseX - (r.x + 1)); value = mAnalyst->GetProcessedValue(xPos, xPos + xStep); } wxString cursor; wxString peak; wxString xpitch; wxString peakpitch; const wxChar *xp; const wxChar *pp; if (mAlg == SpectrumAnalyst::Spectrum) { xpitch = PitchName_Absolute(FreqToMIDInote(xPos)); peakpitch = PitchName_Absolute(FreqToMIDInote(bestpeak)); xp = xpitch; pp = peakpitch; /* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A#*/ cursor.Printf(_("%d Hz (%s) = %d dB"), (int)(xPos + 0.5), xp, (int)(value + 0.5)); peak.Printf(_("%d Hz (%s) = %.1f dB"), (int)(bestpeak + 0.5), pp, bestValue); } else if (xPos > 0.0 && bestpeak > 0.0) { xpitch = PitchName_Absolute(FreqToMIDInote(1.0 / xPos)); peakpitch = PitchName_Absolute(FreqToMIDInote(1.0 / bestpeak)); xp = xpitch; pp = peakpitch; /* i18n-hint: The %d's are replaced by numbers, the %s by musical notes, e.g. A# * the %.4f are numbers, and 'sec' should be an abbreviation for seconds */ cursor.Printf(_("%.4f sec (%d Hz) (%s) = %f"), xPos, (int)(1.0 / xPos + 0.5), xp, value); peak.Printf(_("%.4f sec (%d Hz) (%s) = %.3f"), bestpeak, (int)(1.0 / bestpeak + 0.5), pp, bestValue); } mCursorText->SetValue(cursor); mPeakText->SetValue(peak); } else { mCursorText->SetValue(wxT("")); mPeakText->SetValue(wxT("")); } // Outline the graph dc.SetPen(*wxBLACK_PEN); dc.SetBrush(*wxTRANSPARENT_BRUSH); dc.DrawRectangle(r); }
void EffectChangePitch::Calc_ToOctave() { m_nToOctave = PitchOctave(FreqToMIDInote(m_ToFrequency)); }
// 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(); }