/********************************************************************** Audacity: A Digital Audio Editor Wahwah.cpp Effect programming: Nasca Octavian Paul UI programming: Dominic Mazzoni (with the help of wxDesigner) Vaughan Johnson (Preview) *******************************************************************//** \class EffectWahwah \brief An EffectSimpleMono *//****************************************************************//** \class WahwahDialog \brief Dialog for EffectWahwah *//*******************************************************************/ #include "../Audacity.h" #include "Wahwah.h" #include "../ShuttleGui.h" #include "../WaveTrack.h" #include "../FFT.h" #include #include #include #include #include #include #include // // EffectWahwah // #define lfoskipsamples 30 EffectWahwah::EffectWahwah() { freq = float(1.5); startphase = 0; depth = (float)0.7; freqofs = (float)0.3; res = float(2.5); } wxString EffectWahwah::GetEffectDescription() { // Note: This is useful only after values have been set. return wxString::Format(_("Applied effect: %s frequency = %.1f Hz, start phase = %.0f deg, depth = %.0f%%, resonance = %.1f, frequency offset = %.0f%%"), this->GetEffectName().c_str(), freq, (startphase * 180 / M_PI), (depth * 100), res, (freqofs * 100)); } bool EffectWahwah::PromptUser() { WahwahDialog dlog(this, mParent); dlog.freq = freq; dlog.freqoff = freqofs * 100; dlog.startphase = startphase * 180 / M_PI; dlog.res = res; dlog.depth = depth * 100; dlog.TransferDataToWindow(); dlog.CentreOnParent(); dlog.ShowModal(); if (dlog.GetReturnCode() == wxID_CANCEL) return false; freq = dlog.freq; freqofs = dlog.freqoff / 100; startphase = dlog.startphase * M_PI / 180; res = dlog.res; depth = dlog.depth / 100; return true; } bool EffectWahwah::TransferParameters( Shuttle & shuttle ) { shuttle.TransferFloat(wxT("Freq"),freq,1.5f); shuttle.TransferFloat(wxT("Phase"),startphase,0.0f); shuttle.TransferFloat(wxT("Depth"),depth,0.7f); shuttle.TransferFloat(wxT("Resonance"),res,2.5f); shuttle.TransferFloat(wxT("Offset"),freqofs,0.3f); return true; } bool EffectWahwah::NewTrackSimpleMono() { lfoskip = freq * 2 * M_PI / mCurRate; skipcount = 0; xn1 = 0; xn2 = 0; yn1 = 0; yn2 = 0; b0 = 0; b1 = 0; b2 = 0; a0 = 0; a1 = 0; a2 = 0; phase = startphase; if (mCurChannel == Track::RightChannel) phase += (float)M_PI; return true; } bool EffectWahwah::ProcessSimpleMono(float *buffer, sampleCount len) { float frequency, omega, sn, cs, alpha; float in, out; for (int i = 0; i < len; i++) { in = buffer[i]; if ((skipcount++) % lfoskipsamples == 0) { frequency = (1 + cos(skipcount * lfoskip + phase)) / 2; frequency = frequency * depth * (1 - freqofs) + freqofs; frequency = exp((frequency - 1) * 6); omega = M_PI * frequency; sn = sin(omega); cs = cos(omega); alpha = sn / (2 * res); b0 = (1 - cs) / 2; b1 = 1 - cs; b2 = (1 - cs) / 2; a0 = 1 + alpha; a1 = -2 * cs; a2 = 1 - alpha; }; out = (b0 * in + b1 * xn1 + b2 * xn2 - a1 * yn1 - a2 * yn2) / a0; xn2 = xn1; xn1 = in; yn2 = yn1; yn1 = out; // Prevents clipping if (out < -1.0) out = float(-1.0); else if (out > 1.0) out = float(1.0); buffer[i] = (float) out; } return true; } // WDR: class implementations //---------------------------------------------------------------------------- // WahwahDialog //---------------------------------------------------------------------------- #define FREQ_MIN 1 #define FREQ_MAX 40 #define FREQOFF_MIN 0 #define FREQOFF_MAX 100 #define PHASE_MIN 0 #define PHASE_MAX 359 #define DEPTH_MIN 0 #define DEPTH_MAX 100 #define RES_MIN 0 #define RES_MAX 100 // WDR: event table for WahwahDialog BEGIN_EVENT_TABLE(WahwahDialog, EffectDialog) EVT_BUTTON(ID_EFFECT_PREVIEW, WahwahDialog::OnPreview) EVT_TEXT(ID_FREQTEXT, WahwahDialog::OnFreqText) EVT_TEXT(ID_FREQOFFTEXT, WahwahDialog::OnFreqOffText) EVT_TEXT(ID_PHASETEXT, WahwahDialog::OnPhaseText) EVT_TEXT(ID_DEPTHTEXT, WahwahDialog::OnDepthText) EVT_TEXT(ID_RESONANCETEXT, WahwahDialog::OnResonanceText) EVT_SLIDER(ID_FREQSLIDER, WahwahDialog::OnFreqSlider) EVT_SLIDER(ID_FREQOFFSLIDER, WahwahDialog::OnFreqOffSlider) EVT_SLIDER(ID_PHASESLIDER, WahwahDialog::OnPhaseSlider) EVT_SLIDER(ID_DEPTHSLIDER, WahwahDialog::OnDepthSlider) EVT_SLIDER(ID_RESONANCESLIDER, WahwahDialog::OnResonanceSlider) END_EVENT_TABLE() WahwahDialog::WahwahDialog(EffectWahwah * effect, wxWindow * parent) : EffectDialog(parent, _("Wahwah"), PROCESS_EFFECT), mEffect(effect) { Init(); } void WahwahDialog::PopulateOrExchange(ShuttleGui & S) { S.SetBorder(10); S.StartHorizontalLay(wxCENTER, false); { S.AddTitle(_("by Nasca Octavian Paul")); } S.EndHorizontalLay(); S.SetBorder(5); S.StartMultiColumn(3, wxCENTER); { wxSlider *s; S.Id(ID_FREQTEXT).AddTextBox(_("LFO Frequency (Hz):"), wxT(""), 12); S.SetStyle(wxSL_HORIZONTAL); s = S.Id(ID_FREQSLIDER).AddSlider(wxT(""), 100, FREQ_MAX, FREQ_MIN); s->SetName(_("LFO frequency in hertz")); #if defined(__WXGTK__) s->SetMinSize(wxSize(100, -1)); #endif S.Id(ID_PHASETEXT).AddTextBox(_("LFO Start Phase (deg.):"), wxT(""), 12); S.SetStyle(wxSL_HORIZONTAL); s = S.Id(ID_PHASESLIDER).AddSlider(wxT(""), 0, PHASE_MAX, PHASE_MIN); s->SetName(_("LFO start phase in degrees")); #if defined(__WXGTK__) s->SetMinSize(wxSize(100, -1)); #endif S.Id(ID_DEPTHTEXT).AddTextBox(_("Depth (%):"), wxT(""), 12); S.SetStyle(wxSL_HORIZONTAL); s = S.Id(ID_DEPTHSLIDER).AddSlider(wxT(""), 0, DEPTH_MAX, DEPTH_MIN); s->SetName(_("Depth in percent")); #if defined(__WXGTK__) s->SetMinSize(wxSize(100, -1)); #endif S.Id(ID_RESONANCETEXT).AddTextBox(_("Resonance:"), wxT(""), 12); S.SetStyle(wxSL_HORIZONTAL); s = S.Id(ID_RESONANCESLIDER).AddSlider(wxT(""), 0, RES_MAX, RES_MIN); s->SetName(_("Resonance")); #if defined(__WXGTK__) s->SetMinSize(wxSize(100, -1)); #endif S.Id(ID_FREQOFFTEXT).AddTextBox(_("Wah Frequency Offset (%):"), wxT(""), 12); S.SetStyle(wxSL_HORIZONTAL); s =S.Id(ID_FREQOFFSLIDER).AddSlider(wxT(""), 0, FREQOFF_MAX, FREQOFF_MIN); s->SetName(_("Wah frequency offset in percent")); #if defined(__WXGTK__) s->SetMinSize(wxSize(100, -1)); #endif } S.EndMultiColumn(); } bool WahwahDialog::TransferDataToWindow() { wxSlider *slider; slider = GetFreqSlider(); if (slider) slider->SetValue((int)(freq * 10)); slider = GetFreqOffSlider(); if (slider) slider->SetValue((int)freqoff); slider = GetDepthSlider(); if (slider) slider->SetValue((int)depth); slider = GetPhaseSlider(); if (slider) slider->SetValue((int)startphase); slider = GetResonanceSlider(); if (slider) slider->SetValue((int)(res * 10)); wxTextCtrl *text = GetFreqText(); if (text) { wxString str; str.Printf(wxT("%.1f"), freq); text->SetValue(str); } text = GetFreqOffText(); if (text) { wxString str; str.Printf(wxT("%d"), (int) freqoff); text->SetValue(str); } text = GetPhaseText(); if (text) { wxString str; str.Printf(wxT("%d"), (int) startphase); text->SetValue(str); } text = GetDepthText(); if (text) { wxString str; str.Printf(wxT("%d"), (int) depth); text->SetValue(str); } text = GetResonanceText(); if (text) { wxString str; str.Printf(wxT("%.1f"), res); text->SetValue(str); } return TRUE; } bool WahwahDialog::TransferDataFromWindow() { wxTextCtrl *c; long x; c = GetFreqText(); if (c) { double d; c->GetValue().ToDouble(&d); freq = TrapDouble(d * 10, FREQ_MIN, FREQ_MAX) / 10; } c = GetFreqOffText(); if (c) { double d; c->GetValue().ToDouble(&d); freqoff = TrapDouble(d, FREQOFF_MIN, FREQOFF_MAX); } c = GetPhaseText(); if (c) { c->GetValue().ToLong(&x); startphase = TrapLong(x, PHASE_MIN, PHASE_MAX); } c = GetDepthText(); if (c) { c->GetValue().ToLong(&x); depth = TrapLong(x, DEPTH_MIN, DEPTH_MAX); } c = GetResonanceText(); if (c) { double d; c->GetValue().ToDouble(&d); res = TrapDouble(d * 10, RES_MIN, RES_MAX) / 10; } return TRUE; } // WDR: handler implementations for WahwahDialog void WahwahDialog::OnResonanceSlider(wxCommandEvent & event) { wxString str; long res = GetResonanceSlider()->GetValue(); str.Printf(wxT("%.1f"), res / 10.0); GetResonanceText()->SetValue(str); } void WahwahDialog::OnDepthSlider(wxCommandEvent & event) { wxString str; long depth = GetDepthSlider()->GetValue(); str.Printf(wxT("%ld"), depth); GetDepthText()->SetValue(str); } void WahwahDialog::OnPhaseSlider(wxCommandEvent & event) { wxString str; long phase = GetPhaseSlider()->GetValue(); phase = ((phase + 5) / 10) * 10; // round to nearest multiple of 10 str.Printf(wxT("%ld"), phase); GetPhaseText()->SetValue(str); } void WahwahDialog::OnFreqSlider(wxCommandEvent & event) { wxString str; long freql = GetFreqSlider()->GetValue(); str.Printf(wxT("%.1f"), freql / 10.0); GetFreqText()->SetValue(str); } void WahwahDialog::OnFreqOffSlider(wxCommandEvent & event) { wxString str; long freqoff = GetFreqOffSlider()->GetValue(); str.Printf(wxT("%d"), (int) freqoff); GetFreqOffText()->SetValue(str); } void WahwahDialog::OnResonanceText(wxCommandEvent & event) { wxTextCtrl *c = GetResonanceText(); if (c) { double resd; c->GetValue().ToDouble(&resd); res = resd; res = TrapDouble(resd * 10, RES_MIN, RES_MAX) / 10.0; wxSlider *slider = GetResonanceSlider(); if (slider) slider->SetValue((int)(res * 10)); } } void WahwahDialog::OnDepthText(wxCommandEvent & event) { wxTextCtrl *c = GetDepthText(); if (c) { long depth; c->GetValue().ToLong(&depth); depth = TrapLong(depth, DEPTH_MIN, DEPTH_MAX); wxSlider *slider = GetDepthSlider(); if (slider) slider->SetValue(depth); } } void WahwahDialog::OnPhaseText(wxCommandEvent & event) { wxTextCtrl *c = GetPhaseText(); if (c) { long phase; c->GetValue().ToLong(&phase); phase = TrapLong(phase, PHASE_MIN, PHASE_MAX); wxSlider *slider = GetPhaseSlider(); if (slider) slider->SetValue(phase); } } void WahwahDialog::OnFreqText(wxCommandEvent & event) { wxTextCtrl *c = GetFreqText(); if (c) { long freql; double freqd; c->GetValue().ToDouble(&freqd); freq = freqd; freql = TrapLong(((long) (freq * 10)), FREQ_MIN, FREQ_MAX); wxSlider *slider = GetFreqSlider(); if (slider) slider->SetValue(freql); } } void WahwahDialog::OnFreqOffText(wxCommandEvent & event) { wxTextCtrl *c = GetFreqOffText(); if (c) { double freqoff; c->GetValue().ToDouble(&freqoff); freqoff = TrapDouble(freqoff, FREQOFF_MIN, FREQOFF_MAX); wxSlider *slider = GetFreqOffSlider(); if (slider) slider->SetValue((int)freqoff); } } void WahwahDialog::OnPreview(wxCommandEvent &event) { TransferDataFromWindow(); // Save & restore parameters around Preview, because we didn't do OK. float old_freq = mEffect->freq; float old_freqofs = mEffect->freqofs; float old_startphase = mEffect->startphase; float old_res = mEffect->res; float old_depth = mEffect->depth; mEffect->freq = freq; mEffect->freqofs = freqoff / 100; mEffect->startphase = startphase * M_PI / 180; mEffect->res = res; mEffect->depth = depth / 100; mEffect->Preview(); mEffect->freq = old_freq; mEffect->freqofs = old_freqofs; mEffect->startphase = old_startphase; mEffect->res = old_res; mEffect->depth = old_depth; } // Indentation settings for Vim and Emacs and unique identifier for Arch, a // version control system. Please do not modify past this point. // // Local Variables: // c-basic-offset: 3 // indent-tabs-mode: nil // End: // // vim: et sts=3 sw=3 // arch-tag: b4101b5b-1a18-4f35-9e85-29cc6c1f7f4c