📄 changepitch.cpp
字号:
/********************************************************************** Audacity: A Digital Audio Editor ChangePitch.cpp Vaughan Johnson, Dominic Mazzoni Change Pitch effect provides raising or lowering the pitch without changing the tempo.**********************************************************************/#include "../Audacity.h" // for USE_SOUNDTOUCH#if USE_SOUNDTOUCH#include "SoundTouch.h"#include "ChangePitch.h"#include "../PitchName.h"#include "../Spectrum.h"#include "../WaveTrack.h"#include <math.h>#include <wx/button.h>#include <wx/choice.h>#include <wx/radiobox.h>#include <wx/sizer.h>#include <wx/stattext.h>#include <wx/textctrl.h>#include <wx/valtext.h>//// EffectChangePitch//EffectChangePitch::EffectChangePitch(){ m_FromPitchIndex = -1; // -1 => uninitialized m_bWantPitchDown = false; m_ToPitchIndex = -1; // -1 => uninitialized m_SemitonesChange = 0.0; m_FromFrequency = 0.0; // 0.0 => uninitialized m_ToFrequency = 0.0; // 0.0 => uninitialized m_PercentChange = 0.0;}wxString EffectChangePitch::GetEffectDescription() { // Note: This is useful only after change amount has been set. return wxString::Format(_("Applied effect: %s %.2f semitones"), this->GetEffectName().c_str(), m_SemitonesChange); } bool EffectChangePitch::Init(){ mSoundTouch = NULL; return true;}// 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. TrackListIterator iter(mWaveTracks); 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; longSampleCount 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/2, (int)rate, 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(Freq2Pitch(m_FromFrequency)); m_bWantPitchDown = (m_ToFrequency < m_FromFrequency); m_ToPitchIndex = PitchIndex(Freq2Pitch(m_ToFrequency)); }}bool EffectChangePitch::PromptUser(){ this->DeduceFrequencies(); // Set frequency-related control values based on sample. ChangePitchDialog dlog(this, mParent, -1, _("Change Pitch")); dlog.m_FromPitchIndex = m_FromPitchIndex; dlog.m_bWantPitchDown = m_bWantPitchDown; dlog.m_ToPitchIndex = m_ToPitchIndex; dlog.m_SemitonesChange = m_SemitonesChange; dlog.m_FromFrequency = m_FromFrequency; dlog.m_ToFrequency = m_ToFrequency; dlog.m_PercentChange = m_PercentChange; // Don't need to call TransferDataToWindow, although other // Audacity dialogs (from which I derived this one) do it, because // ShowModal calls stuff that eventually calls wxWindowBase::OnInitDialog, // which calls dlog.TransferDataToWindow(); dlog.CentreOnParent(); dlog.ShowModal(); if (!dlog.GetReturnCode()) return false; m_FromPitchIndex = dlog.m_FromPitchIndex; m_bWantPitchDown = dlog.m_bWantPitchDown; m_ToPitchIndex = dlog.m_ToPitchIndex; m_SemitonesChange = dlog.m_SemitonesChange; m_FromFrequency = dlog.m_FromFrequency; m_ToFrequency = dlog.m_ToFrequency; m_PercentChange = dlog.m_PercentChange; return true;}bool EffectChangePitch::TransferParameters( Shuttle & shuttle ){ shuttle.TransferDouble(wxT("Percentage"),m_PercentChange,0.0); return true;}bool EffectChangePitch::Process(){ mSoundTouch = new SoundTouch(); mSoundTouch->setPitchSemiTones((float)(m_SemitonesChange)); return this->EffectSoundTouch::Process();}//----------------------------------------------------------------------------// ChangePitchDialog//----------------------------------------------------------------------------#define PERCENTCHANGE_MIN -99#define PERCENTCHANGE_MAX 100 // warped above zero to actually go up to 400%#define PERCENTCHANGE_SLIDER_WARP 1.30105 // warp power takes max from 100 to 400.enum { ID_TEXT_PERCENTCHANGE = 10001, ID_SLIDER_PERCENTCHANGE, ID_CHOICE_FROMPITCH, ID_RADIOBOX_PITCHUPDOWN, ID_CHOICE_TOPITCH, ID_TEXT_SEMITONESCHANGE, ID_TEXT_FROMFREQUENCY, ID_TEXT_TOFREQUENCY, ID_BUTTON_PREVIEW};// event table for ChangePitchDialogBEGIN_EVENT_TABLE(ChangePitchDialog, wxDialog) EVT_BUTTON(wxID_OK, ChangePitchDialog::OnOk) EVT_BUTTON(wxID_CANCEL, ChangePitchDialog::OnCancel) EVT_CHOICE(ID_CHOICE_FROMPITCH, ChangePitchDialog::OnChoice_FromPitch) EVT_RADIOBOX(ID_RADIOBOX_PITCHUPDOWN, ChangePitchDialog::OnRadioBox_PitchUpDown) EVT_CHOICE(ID_CHOICE_TOPITCH, ChangePitchDialog::OnChoice_ToPitch) EVT_TEXT(ID_TEXT_SEMITONESCHANGE, ChangePitchDialog::OnText_SemitonesChange) EVT_TEXT(ID_TEXT_FROMFREQUENCY, ChangePitchDialog::OnText_FromFrequency) EVT_TEXT(ID_TEXT_TOFREQUENCY, ChangePitchDialog::OnText_ToFrequency) EVT_TEXT(ID_TEXT_PERCENTCHANGE, ChangePitchDialog::OnText_PercentChange) EVT_SLIDER(ID_SLIDER_PERCENTCHANGE, ChangePitchDialog::OnSlider_PercentChange) EVT_BUTTON(ID_BUTTON_PREVIEW, ChangePitchDialog::OnPreview)END_EVENT_TABLE()ChangePitchDialog::ChangePitchDialog(EffectChangePitch * effect, wxWindow * parent, wxWindowID id, const wxString & title, const wxPoint & position, const wxSize & size, long style): wxDialog(parent, id, title, position, size, style){ m_bLoopDetect = false; m_pEffect = effect; // NULL out these control members because there are some cases where the // event table handlers get called during this method, and those handlers that // can cause trouble check for NULL. m_pChoice_FromPitch = NULL; m_pRadioBox_PitchUpDown = NULL; m_pChoice_ToPitch = NULL; m_pTextCtrl_SemitonesChange = NULL; m_pTextCtrl_FromFrequency = NULL; m_pTextCtrl_ToFrequency = NULL; m_pTextCtrl_PercentChange = NULL; m_pSlider_PercentChange = NULL; // effect parameters m_FromPitchIndex = -1; // -1 => uninitialized m_bWantPitchDown = false; m_ToPitchIndex = -1; // -1 => uninitialized m_SemitonesChange = 0.0; m_FromFrequency = 0.0; // 0.0 => uninitialized m_ToFrequency = 0.0; // 0.0 => uninitialized m_PercentChange = 0.0; // CREATE THE CONTROLS PROGRAMMATICALLY. wxStaticText * pStaticText; wxBoxSizer * pBoxSizer_Dialog = new wxBoxSizer(wxVERTICAL); // heading pStaticText = new wxStaticText(this, -1, _("Change Pitch without Changing Tempo"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Dialog->Add(pStaticText, 0, wxALIGN_CENTER | wxALL, 8); pStaticText = new wxStaticText(this, -1, _("by Vaughan Johnson && Dominic Mazzoni"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Dialog->Add(pStaticText, 0, wxALIGN_CENTER | wxTOP | wxLEFT | wxRIGHT, 8); pStaticText = new wxStaticText(this, -1, _("using SoundTouch, by Olli Parviainen"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Dialog->Add(pStaticText, 0, wxALIGN_CENTER | wxBOTTOM | wxLEFT | wxRIGHT, 8); pBoxSizer_Dialog->Add(0, 8, 0); // spacer // from/to pitch controls wxBoxSizer * pBoxSizer_Pitch = new wxBoxSizer(wxHORIZONTAL); pStaticText = new wxStaticText(this, -1, _("Pitch: from"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Pitch->Add(pStaticText, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 4); const wxString strArray_PitchNames[] = {wxT("C"), wxT("C#/Db"), wxT("D"), wxT("D#/Eb"), wxT("E"), wxT("F"), wxT("F#/Gb"), wxT("G"), wxT("G#/Ab"), wxT("A"), wxT("A#/Bb"), wxT("B")}; const int numChoicesPitchNames = 12; m_pChoice_FromPitch = new wxChoice(this, ID_CHOICE_FROMPITCH, wxDefaultPosition, wxSize(64, -1), numChoicesPitchNames, strArray_PitchNames); pBoxSizer_Pitch->Add(m_pChoice_FromPitch, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 4); const wxString strArray_RadioPitchUpDown[] = {_("up"), _("down")}; m_pRadioBox_PitchUpDown = new wxRadioBox(this, ID_RADIOBOX_PITCHUPDOWN, wxT(""), wxDefaultPosition, wxDefaultSize, 2, strArray_RadioPitchUpDown, 1); pBoxSizer_Pitch->Add(m_pRadioBox_PitchUpDown, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 4); pStaticText = new wxStaticText(this, -1, _("to"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Pitch->Add(pStaticText, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 4); m_pChoice_ToPitch = new wxChoice(this, ID_CHOICE_TOPITCH, wxDefaultPosition, wxSize(64, -1), numChoicesPitchNames, strArray_PitchNames); pBoxSizer_Pitch->Add(m_pChoice_ToPitch, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 4); pBoxSizer_Dialog->Add(pBoxSizer_Pitch, 0, wxALIGN_CENTER | wxALL, 4); // semitones change controls wxBoxSizer * pBoxSizer_SemitonesChange = new wxBoxSizer(wxHORIZONTAL); pStaticText = new wxStaticText(this, -1, _("Semitones (half-steps):"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_SemitonesChange->Add(pStaticText, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 4); m_pTextCtrl_SemitonesChange = new wxTextCtrl(this, ID_TEXT_SEMITONESCHANGE, wxT("0.0"), wxDefaultPosition, wxSize(40, -1), 0, wxTextValidator(wxFILTER_NUMERIC)); pBoxSizer_SemitonesChange->Add(m_pTextCtrl_SemitonesChange, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 4); pBoxSizer_Dialog->Add(pBoxSizer_SemitonesChange, 0, wxALIGN_CENTER | wxALL, 4); // from/to frequency controls pBoxSizer_Dialog->Add(0, 8, 0); // spacer wxBoxSizer * pBoxSizer_Frequency = new wxBoxSizer(wxHORIZONTAL); pStaticText = new wxStaticText(this, -1, _("Frequency (Hz): from"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Frequency->Add(pStaticText, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 4); m_pTextCtrl_FromFrequency = new wxTextCtrl(this, ID_TEXT_FROMFREQUENCY, wxT(""), wxDefaultPosition, wxSize(64, -1), 0, wxTextValidator(wxFILTER_NUMERIC)); pBoxSizer_Frequency->Add(m_pTextCtrl_FromFrequency, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 4); pStaticText = new wxStaticText(this, -1, _("to"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_Frequency->Add(pStaticText, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 4); m_pTextCtrl_ToFrequency = new wxTextCtrl(this, ID_TEXT_TOFREQUENCY, wxT(""), wxDefaultPosition, wxSize(64, -1), 0, wxTextValidator(wxFILTER_NUMERIC)); pBoxSizer_Frequency->Add(m_pTextCtrl_ToFrequency, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 4); pBoxSizer_Dialog->Add(pBoxSizer_Frequency, 0, wxALIGN_CENTER | wxALL, 4); // percent change control // Group percent controls with spacers, // rather than static box, so they don't look isolated. pBoxSizer_Dialog->Add(0, 8, 0); // spacer wxBoxSizer * pBoxSizer_PercentChange = new wxBoxSizer(wxHORIZONTAL); pStaticText = new wxStaticText(this, -1, _("Percent Change:"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_PercentChange->Add(pStaticText, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 4); //v Override wxTextValidator to disallow negative values < -100.0? m_pTextCtrl_PercentChange = new wxTextCtrl(this, ID_TEXT_PERCENTCHANGE, wxT("0.0"), wxDefaultPosition, wxSize(40, -1), 0, wxTextValidator(wxFILTER_NUMERIC)); pBoxSizer_PercentChange->Add(m_pTextCtrl_PercentChange, 0, wxALIGN_CENTER_VERTICAL | wxALL, 4); pBoxSizer_Dialog->Add(pBoxSizer_PercentChange, 0, wxALIGN_CENTER | wxALL, 4); m_pSlider_PercentChange = new wxSlider(this, ID_SLIDER_PERCENTCHANGE, 0, PERCENTCHANGE_MIN, PERCENTCHANGE_MAX, wxDefaultPosition, wxSize(100, -1), wxSL_HORIZONTAL); pBoxSizer_Dialog->Add(m_pSlider_PercentChange, 1, wxGROW | wxALIGN_CENTER | wxLEFT | wxRIGHT, 4); pBoxSizer_Dialog->Add(0, 8, 0); // spacer // Preview, OK, & Cancel buttons pBoxSizer_Dialog->Add(0, 8, 0); // spacer wxBoxSizer * pBoxSizer_OK = new wxBoxSizer(wxHORIZONTAL); wxButton * pButton_Preview = new wxButton(this, ID_BUTTON_PREVIEW, m_pEffect->GetPreviewName()); pBoxSizer_OK->Add(pButton_Preview, 0, wxALIGN_CENTER | wxALL, 4); pBoxSizer_OK->Add(32, 8); // horizontal spacer wxButton * pButton_Cancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0); pBoxSizer_OK->Add(pButton_Cancel, 0, wxALIGN_CENTER | wxALL, 4); wxButton * pButton_OK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0); pButton_OK->SetDefault(); pButton_OK->SetFocus(); pBoxSizer_OK->Add(pButton_OK, 0, wxALIGN_CENTER | wxALL, 4); pBoxSizer_Dialog->Add(pBoxSizer_OK, 0, wxALIGN_CENTER | wxALL, 8); this->SetAutoLayout(true); this->SetSizer(pBoxSizer_Dialog); pBoxSizer_Dialog->Fit(this); pBoxSizer_Dialog->SetSizeHints(this);}bool ChangePitchDialog::Validate(){ return true; }bool ChangePitchDialog::TransferDataToWindow(){ m_bLoopDetect = true; // from/to pitch controls if (m_pChoice_FromPitch) m_pChoice_FromPitch->SetSelection(m_FromPitchIndex); this->Update_RadioBox_PitchUpDown(); this->Update_Choice_ToPitch(); // semitones change control this->Update_Text_SemitonesChange();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -