From 5555c2db6239aaac15f63646145f00d1ee12329d Mon Sep 17 00:00:00 2001 From: "v.audacity" Date: Thu, 2 May 2013 23:41:27 +0000 Subject: [PATCH] Rob Sykes's patch (considerably modified because of MSVC preprocessor problems) to add sox-based reverb --- src/Makefile.in | 1 + src/ShuttleGui.cpp | 20 +- src/ShuttleGui.h | 10 +- src/effects/Effect.cpp | 31 +- src/effects/Effect.h | 6 +- src/effects/LoadEffects.cpp | 2 + src/effects/Reverb.cpp | 577 ++++++++++++++++++++++++++ src/effects/Reverb.h | 152 +++++++ src/effects/Reverb_libSoX.h | 270 ++++++++++++ win/Projects/Audacity/Audacity.vcproj | 12 + 10 files changed, 1059 insertions(+), 22 deletions(-) create mode 100644 src/effects/Reverb.cpp create mode 100644 src/effects/Reverb.h create mode 100644 src/effects/Reverb_libSoX.h diff --git a/src/Makefile.in b/src/Makefile.in index 4dac2ae2b..e5f356421 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -172,6 +172,7 @@ OBJS = \ effects/Phaser.o \ effects/Repair.o \ effects/Repeat.o \ + effects/Reverb.o \ effects/Reverse.o \ effects/Silence.o \ effects/StereoToMono.o \ diff --git a/src/ShuttleGui.cpp b/src/ShuttleGui.cpp index 2720532a4..80f10a1b9 100644 --- a/src/ShuttleGui.cpp +++ b/src/ShuttleGui.cpp @@ -393,7 +393,7 @@ wxStaticText * ShuttleGuiBase::AddVariableText(const wxString &Str, bool bCenter return pStatic; } -wxComboBox * ShuttleGuiBase::AddCombo( const wxString &Prompt, const wxString &Selected,const wxArrayString * pChoices ) +wxComboBox * ShuttleGuiBase::AddCombo( const wxString &Prompt, const wxString &Selected,const wxArrayString * pChoices, long style ) { UseUpId(); if( mShuttleMode != eIsCreating ) @@ -413,7 +413,7 @@ wxComboBox * ShuttleGuiBase::AddCombo( const wxString &Prompt, const wxString &S AddPrompt( Prompt ); mpWind = pCombo = new wxComboBox(mpParent, miId, Selected, wxDefaultPosition, wxDefaultSize, - n, Choices, Style( 0 )); + n, Choices, Style( style )); mpWind->SetName(wxStripMenuCodes(Prompt)); UpdateSizers(); @@ -2176,16 +2176,28 @@ wxSizer *CreateStdButtonSizer(wxWindow *parent, long buttons, wxButton *extra) bs->AddButton( new wxButton( parent, wxID_HELP ) ); } - if( buttons & ePreviewButton ) + if( (buttons & ePreviewButton) && (buttons & ePreviewDryButton) ) + { + bs->Add(new wxButton( parent, ePreviewID, _("Pre&view") ) ); + bs->Add(new wxButton( parent, ePreviewDryID, _("Dry Previe&w") ) ); + bs->Add( 20, 0 ); + } + else if( buttons & ePreviewButton ) { b = new wxButton( parent, ePreviewID, _("Pre&view") ); bs->Add( b, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, margin ); bs->Add( 40, 0 ); } + if( buttons & eDefaultsButton ) + { + bs->Add(new wxButton( parent, eDefaultsID, _("&Defaults") ) ); + bs->Add( 20, 0 ); + } + if( buttons & eDebugButton ) { - b = new wxButton( parent, eDebugID, _("&Debug") ); + b = new wxButton( parent, eDebugID, _("Debu&g") ); bs->Add( b, 0, wxALIGN_CENTER | wxLEFT | wxRIGHT, margin ); bs->Add( 40, 0 ); } diff --git a/src/ShuttleGui.h b/src/ShuttleGui.h index 0270f0bbb..aaa9c9344 100644 --- a/src/ShuttleGui.h +++ b/src/ShuttleGui.h @@ -99,7 +99,7 @@ public: wxGrid * AddGrid(); wxCheckBox * AddCheckBox( const wxString &Prompt, const wxString &Selected); wxCheckBox * AddCheckBoxOnRight( const wxString &Prompt, const wxString &Selected); - wxComboBox * AddCombo( const wxString &Prompt, const wxString &Selected,const wxArrayString * pChoices ); + wxComboBox * AddCombo( const wxString &Prompt, const wxString &Selected,const wxArrayString * pChoices, long style = 0 ); wxChoice * AddChoice( const wxString &Prompt, const wxString &Selected, const wxArrayString * pChoices ); wxMenuBar * AddMenuBar( ); wxMenu * AddMenu( const wxString & Title ); @@ -333,13 +333,17 @@ enum eNoButton = 0x0008, eHelpButton = 0x0010, ePreviewButton = 0x0020, - eDebugButton = 0x0040 + eDebugButton = 0x0040, + eDefaultsButton= 0x0080, + ePreviewDryButton = 0x0100 }; enum { ePreviewID = wxID_LOWEST - 1, - eDebugID = wxID_LOWEST - 2 + eDebugID = wxID_LOWEST - 2, + eDefaultsID = wxID_LOWEST - 3, + ePreviewDryID = wxID_LOWEST - 4 }; AUDACITY_DLL_API wxSizer *CreateStdButtonSizer( wxWindow *parent, diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 9543b1e1d..6cea8cdc2 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -413,7 +413,7 @@ wxString Effect::GetPreviewName() return _("Pre&view"); } -void Effect::Preview() +void Effect::Preview(bool dryOnly) { wxWindow* FocusDialog = wxWindow::FindFocus(); if (gAudioIO->IsBusy()) @@ -468,16 +468,19 @@ void Effect::Preview() // Apply effect - // Effect is already inited; we call Process, End, and then Init - // again, so the state is exactly the way it was before Preview - // was called. - mProgress = new ProgressDialog(StripAmpersand(GetEffectName()), - _("Preparing preview"), - pdlgHideCancelButton); // Have only "Stop" button. - bool bSuccess = Process(); - delete mProgress; - End(); - Init(); + bool bSuccess(true); + if (!dryOnly) { + // Effect is already inited; we call Process, End, and then Init + // again, so the state is exactly the way it was before Preview + // was called. + mProgress = new ProgressDialog(StripAmpersand(GetEffectName()), + _("Preparing preview"), + pdlgHideCancelButton); // Have only "Stop" button. + bSuccess = Process(); + delete mProgress; + End(); + Init(); + } if (bSuccess) { mT0 = t0save; @@ -546,10 +549,12 @@ void Effect::Preview() EffectDialog::EffectDialog(wxWindow * parent, const wxString & title, int type, - int flags) + int flags, + int additionalButtons) : wxDialog(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, flags) { mType = type; + mAdditionalButtons = additionalButtons; } void EffectDialog::Init() @@ -571,7 +576,7 @@ void EffectDialog::Init() buttons |= ePreviewButton; } } - S.AddStandardButtons(buttons); + S.AddStandardButtons(buttons|mAdditionalButtons); } S.EndVerticalLay(); diff --git a/src/effects/Effect.h b/src/effects/Effect.h index 6ad732324..0f08eb2a2 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -126,7 +126,7 @@ class AUDACITY_DLL_API Effect { // The Effect class fully implements the Preview method for you. // Only override it if you need to do preprocessing or cleanup. - virtual void Preview(); + virtual void Preview(bool dryOnly = false); // Most effects just use the previewLength, but time-stretching/compressing // effects need to use a different input length, so override this method. @@ -312,7 +312,8 @@ public: EffectDialog(wxWindow * parent, const wxString & title, int type = PROCESS_EFFECT, - int flags = wxDEFAULT_DIALOG_STYLE); + int flags = wxDEFAULT_DIALOG_STYLE, + int additionalButtons = 0); void Init(); @@ -324,6 +325,7 @@ public: private: int mType; + int mAdditionalButtons; }; // Utility functions diff --git a/src/effects/LoadEffects.cpp b/src/effects/LoadEffects.cpp index 3a89ff152..dc6269e3c 100644 --- a/src/effects/LoadEffects.cpp +++ b/src/effects/LoadEffects.cpp @@ -35,6 +35,7 @@ #include "Phaser.h" #include "Repair.h" #include "Repeat.h" +#include "Reverb.h" #include "Reverse.h" #include "Silence.h" #include "StereoToMono.h" @@ -260,6 +261,7 @@ void LoadEffects() em.RegisterEffect(new EffectPhaser()); em.RegisterEffect(new EffectRepair()); em.RegisterEffect(new EffectRepeat()); + em.RegisterEffect(new EffectReverb()); em.RegisterEffect(new EffectReverse()); em.RegisterEffect(new EffectStereoToMono(), HIDDEN_EFFECT);// NOT in normal effects list. em.RegisterEffect(new EffectTruncSilence(), SIMPLE_EFFECT); diff --git a/src/effects/Reverb.cpp b/src/effects/Reverb.cpp new file mode 100644 index 000000000..b1fa37020 --- /dev/null +++ b/src/effects/Reverb.cpp @@ -0,0 +1,577 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + Audacity(R) is copyright (c) 1999-2013 Audacity Team. + License: GPL v2. See License.txt. + + Reverb.cpp + Rob Sykes, Vaughan Johnson + +******************************************************************//** + +\class EffectReverb +\brief A reverberation effect + +*//****************************************************************//** + +\class ReverbDialogue +\brief A configuration class used by effect, EffectReverb. + +*//*******************************************************************/ + +#include "Reverb.h" +#include "Reverb_libSoX.h" +#include "../Prefs.h" + +// +// EffectReverb +// + +struct Reverb_priv_t { + size_t ichannels, ochannels; + struct { + reverb_t reverb; + float * dry, * wet[2]; + } chan[2]; +}; + +void EffectReverb::Create(double rate, bool isStereo) +{ + mP = (Reverb_priv_t *)calloc(sizeof *mP, 1); + size_t i; + +#define BLOCK 0x4000u + mP->ichannels = mP->ochannels = 1 + isStereo; + for (i = 0; i < mP->ichannels; ++i) + reverb_create( + &mP->chan[i].reverb, rate, mParams.mWetGain, mParams.mRoomSize, + mParams.mReverberance, mParams.mHfDamping, mParams.mDelay, mParams.mStereoWidth*isStereo, + mParams.mToneLow, mParams.mToneHigh, BLOCK, mP->chan[i].wet); +} + +bool EffectReverb::ProcessOneBlock(sampleCount len0, float * const * chans0) +{ + float * chans[2]; + chans[0] = chans0[0], chans[1] = chans0[1]; + size_t c, i, w, len = len0; + float const dryMult(mParams.mWetOnly? 0 : dB_to_linear(mParams.mDryGain)); + while (len) { + size_t len1 = min(len, BLOCK); + for (c = 0; c < mP->ichannels; ++c) { + mP->chan[c].dry = (float *)fifo_write(&mP->chan[c].reverb.input_fifo, len1, chans[c]); + reverb_process(&mP->chan[c].reverb, len1); + } + if (mP->ichannels == 2) + for (i = 0; i < len1; ++i) + for (w = 0; w < 2; ++w) + chans[w][i] = dryMult * mP->chan[w].dry[i] + + .5 * (mP->chan[0].wet[w][i] + mP->chan[1].wet[w][i]); + else for (i = 0; i < len1; ++i) + for (w = 0; w < mP->ochannels; ++w) + chans[0][i] = dryMult * mP->chan[0].dry[i] + mP->chan[0].wet[w][i]; + len -= len1; + for (c = 0; c < mP->ichannels; chans[c++] += len1); + } + return true; +} + +void EffectReverb::Delete() +{ + for (size_t i = 0; i < mP->ichannels; reverb_delete(&mP->chan[i++].reverb)); + free(mP); +} + +// Most of what follows should really be provided by Audacity framework classes: +//vvvvv Not sure what you mean by that, Rob. A la EffectSimpleMono, e.g.? + +bool EffectReverb::ProcessOneTrack(size_t n, WaveTrack * track, WaveTrack * track2, wxString const & msg) +{ + sampleCount begin = track->TimeToLongSamples(mCurT0), pos = begin; + sampleCount end = track->TimeToLongSamples(mCurT1); + float * buffers[2]; + buffers[0] = new float[track->GetMaxBlockSize()]; + buffers[1] = track2? new float[track->GetMaxBlockSize()] : 0; + bool cancelled = false; + + Create(track->GetRate(), track2 != 0); + while (!cancelled && pos < end) { + sampleCount block = track->GetBestBlockSize(pos); + block = min(block, end - pos); + track->Get((samplePtr) buffers[0], floatSample, pos, block); + if (track2) + track2->Get((samplePtr) buffers[1], floatSample, pos, block); + ProcessOneBlock(block, buffers); + track->Set((samplePtr) buffers[0], floatSample, pos, block); + if (track2) + track2->Set((samplePtr) buffers[1], floatSample, pos, block); + pos += block; + cancelled = TrackProgress(n, (1. * pos - begin) / (1. * end - begin) * .5 + .5, msg); + } + Delete(); + delete[] buffers[0]; + delete[] buffers[1]; + return !cancelled; +} + +bool EffectReverb::Process() +{ + CopyInputTracks(); // Set up mOutputTracks. + SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks); + WaveTrack * track = (WaveTrack *)iter.First(); + bool success = true; + for (int n = 0; success && track; track = (WaveTrack *)iter.Next(), ++n) { + mCurT0 = max(mT0, track->GetStartTime()); + mCurT1 = min(mT1, track->GetEndTime()); + if (mCurT1 > mCurT0) { + wxString msg(_("Processing: ") + track->GetName()); + if (track->GetLinked() && mParams.mStereoWidth) // do stereo? + success = ProcessOneTrack(++n, track, (WaveTrack *)iter.Next(), msg); + else success = ProcessOneTrack(n, track, 0, msg); + } + } + ReplaceProcessedTracks(success); + return success; +} + +wxString EffectReverb::SettingsPath(int settingsNumber) const +{ + wxString x(wxString::FromAscii("/Effects/Reverb/")); + if (settingsNumber >= 0) + x += wxString::Format(wxString::FromAscii("%i/"), settingsNumber); + return x; +} + +wxString EffectReverb::SettingsName(int settingsNumber) const +{ + wxString x(SettingsPath(settingsNumber)); + gPrefs->Read(x + wxT("name"), &x, wxString::Format(wxString::FromAscii("Settings%i"), settingsNumber)); + return x; +} + +void EffectReverb::LoadSettings(int settingsNumber, EffectReverb::Params & params) +{ + wxString sSettingsPath(SettingsPath(settingsNumber)); + gPrefs->Read(sSettingsPath + wxT("RoomSize"), ¶ms.mRoomSize, 75); + gPrefs->Read(sSettingsPath + wxT("Delay"), ¶ms.mDelay, 10); + gPrefs->Read(sSettingsPath + wxT("Reverberance"), ¶ms.mReverberance, 50); + gPrefs->Read(sSettingsPath + wxT("HfDamping"), ¶ms.mHfDamping, 50); + gPrefs->Read(sSettingsPath + wxT("ToneLow"), ¶ms.mToneLow, 100); + gPrefs->Read(sSettingsPath + wxT("ToneHigh"), ¶ms.mToneHigh, 100); + gPrefs->Read(sSettingsPath + wxT("WetGain"), ¶ms.mWetGain, -1); + gPrefs->Read(sSettingsPath + wxT("DryGain"), ¶ms.mDryGain, -1); + gPrefs->Read(sSettingsPath + wxT("StereoWidth"), ¶ms.mStereoWidth, 100); + gPrefs->Read(sSettingsPath + wxT("WetOnly"), ¶ms.mWetOnly, 0); +} + +void EffectReverb::SaveSettings(int settingsNumber, EffectReverb::Params const * params, wxString const * name) const +{ + wxString sSettingsPath(SettingsPath(settingsNumber)); + if (name) + gPrefs->Write(sSettingsPath + wxT("name"), *name); + if (params) + { + gPrefs->Write(sSettingsPath + wxT("RoomSize"), params->mRoomSize); + gPrefs->Write(sSettingsPath + wxT("Delay"), params->mDelay); + gPrefs->Write(sSettingsPath + wxT("Reverberance"), params->mReverberance); + gPrefs->Write(sSettingsPath + wxT("HfDamping"), params->mHfDamping); + gPrefs->Write(sSettingsPath + wxT("ToneLow"), params->mToneLow); + gPrefs->Write(sSettingsPath + wxT("ToneHigh"), params->mToneHigh); + gPrefs->Write(sSettingsPath + wxT("WetGain"), params->mWetGain); + gPrefs->Write(sSettingsPath + wxT("DryGain"), params->mDryGain); + gPrefs->Write(sSettingsPath + wxT("StereoWidth"), params->mStereoWidth); + gPrefs->Write(sSettingsPath + wxT("WetOnly"), params->mWetOnly); + } + gPrefs->Flush(); +} + +EffectReverb::EffectReverb() +{ + LoadSettings(-1, mParams); +} + +wxString EffectReverb::GetEffectDescription() +{ + wxString strResult = + wxString::Format(_("Applied effect: %s"), GetEffectName().c_str()); + strResult += wxString::Format(_(", Room Size = %.0f"), mParams.mRoomSize); + strResult += wxString::Format(_(", Delay = %.0fms"), mParams.mDelay); + strResult += wxString::Format(_(", Reverberance = %.0f%%"), mParams.mReverberance); + strResult += wxString::Format(_(", Damping = %.0f%%"), mParams.mHfDamping); + strResult += wxString::Format(_(", Tone Low = %.0f%%"), mParams.mToneLow); + strResult += wxString::Format(_(", Tone High = %.0f%%"), mParams.mToneHigh); + strResult += wxString::Format(_(", Wet Gain = %.0fdB"), mParams.mWetGain); + strResult += wxString::Format(_(", Dry Gain Size = %.0fdB"), mParams.mDryGain); + strResult += wxString::Format(_(", Stereo Width = %.0f%%"), mParams.mStereoWidth); + strResult += wxString::Format(_(", Wet Only = %s"), mParams.mWetOnly ? _("true") : _("false")); + return strResult; +} + +bool EffectReverb::PromptUser() +{ + ReverbDialogue d(this, mParent); + d.CentreOnParent(); + d.ShowModal(); + if (d.GetReturnCode() == wxID_CANCEL) + return false; + SaveSettings(-1, &mParams); + return true; +} + +bool EffectReverb::TransferParameters(Shuttle & shuttle) +{ + shuttle.TransferDouble(wxT("RoomSize"), mParams.mRoomSize, 75); + shuttle.TransferDouble(wxT("Delay"), mParams.mDelay, 10); + shuttle.TransferDouble(wxT("Reverberance"), mParams.mReverberance, 50); + shuttle.TransferDouble(wxT("HfDamping"), mParams.mHfDamping, 50); + shuttle.TransferDouble(wxT("ToneLow"), mParams.mToneLow, 100); + shuttle.TransferDouble(wxT("ToneHigh"), mParams.mToneHigh, 100); + shuttle.TransferDouble(wxT("WetGain"), mParams.mWetGain, -1); + shuttle.TransferDouble(wxT("DryGain"), mParams.mDryGain, -1); + shuttle.TransferDouble(wxT("StereoWidth"), mParams.mStereoWidth, 100); + + shuttle.TransferBool(wxT("WetOnly"), mParams.mWetOnly, 0); + + return true; +} + +//---------------------------------------------------------------------------- +// ReverbDialogue +//---------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +enum {ID_START = 10000, + ID_PRESETS, + ID_LOAD_SETTINGS, + ID_SAVE_SETTINGS, + ID_RENAME_SETTINGS, + + ID_RoomSize_TEXT, ID_RoomSize_WIDGET, + ID_Delay_TEXT, ID_Delay_WIDGET, + ID_Reverberance_TEXT, ID_Reverberance_WIDGET, + ID_HfDamping_TEXT, ID_HfDamping_WIDGET, + ID_ToneLow_TEXT, ID_ToneLow_WIDGET, + ID_ToneHigh_TEXT, ID_ToneHigh_WIDGET, + ID_WetGain_TEXT, ID_WetGain_WIDGET, + ID_DryGain_TEXT, ID_DryGain_WIDGET, + ID_StereoWidth_TEXT, ID_StereoWidth_WIDGET, + + ID_WetOnly_WIDGET, + + ID_END}; + +BEGIN_EVENT_TABLE(ReverbDialogue, EffectDialog) + EVT_SLIDER(ID_RoomSize_WIDGET, ReverbDialogue::OnRoomSizeWidget) + EVT_TEXT(ID_RoomSize_TEXT, ReverbDialogue::OnRoomSizeText) + + EVT_SLIDER(ID_Delay_WIDGET, ReverbDialogue::OnDelayWidget) + EVT_TEXT(ID_Delay_TEXT, ReverbDialogue::OnDelayText) + + EVT_SLIDER(ID_Reverberance_WIDGET, ReverbDialogue::OnReverberanceWidget) + EVT_TEXT(ID_Reverberance_TEXT, ReverbDialogue::OnReverberanceText) + + EVT_SLIDER(ID_HfDamping_WIDGET, ReverbDialogue::OnHfDampingWidget) + EVT_TEXT(ID_HfDamping_TEXT, ReverbDialogue::OnHfDampingText) + + EVT_SLIDER(ID_ToneLow_WIDGET, ReverbDialogue::OnToneLowWidget) + EVT_TEXT(ID_ToneLow_TEXT, ReverbDialogue::OnToneLowText) + + EVT_SLIDER(ID_ToneHigh_WIDGET, ReverbDialogue::OnToneHighWidget) + EVT_TEXT(ID_ToneHigh_TEXT, ReverbDialogue::OnToneHighText) + + EVT_SLIDER(ID_WetGain_WIDGET, ReverbDialogue::OnWetGainWidget) + EVT_TEXT(ID_WetGain_TEXT, ReverbDialogue::OnWetGainText) + + EVT_SLIDER(ID_DryGain_WIDGET, ReverbDialogue::OnDryGainWidget) + EVT_TEXT(ID_DryGain_TEXT, ReverbDialogue::OnDryGainText) + + EVT_SLIDER(ID_StereoWidth_WIDGET, ReverbDialogue::OnStereoWidthWidget) + EVT_TEXT(ID_StereoWidth_TEXT, ReverbDialogue::OnStereoWidthText) + + EVT_BUTTON(ePreviewID, ReverbDialogue::OnPreview) + EVT_BUTTON(ePreviewDryID, ReverbDialogue::OnPreview) + EVT_BUTTON(ID_PRESETS, ReverbDialogue::LoadPreset) + EVT_BUTTON(ID_SAVE_SETTINGS, ReverbDialogue::SaveSettings) + EVT_BUTTON(ID_LOAD_SETTINGS, ReverbDialogue::LoadSettings) + EVT_BUTTON(ID_RENAME_SETTINGS, ReverbDialogue::RenameSettings) +END_EVENT_TABLE() + +ReverbDialogue::ReverbDialogue(EffectReverb * effect, wxWindow * parent): + EffectDialog(parent, _(""), PROCESS_EFFECT, wxDEFAULT_DIALOG_STYLE, ePreviewDryButton), + mEffect(*effect), mParams(effect->mParams) +{ + SetTitle(); + Init(); +} + +void ReverbDialogue::PopulateOrExchange(ShuttleGui & s) +{ + s.StartHorizontalLay(wxCENTER, false); + s.AddTitle(_("by Rob Sykes")); /* i18n-hint: Rob Sykes is a person's name.*/ + s.EndHorizontalLay(); + s.StartHorizontalLay(wxCENTER, false); // Add a little space + s.EndHorizontalLay(); + + s.StartMultiColumn(3, wxEXPAND); + { + s.SetStretchyCol(2); + //vvvvv This one a little tricky so leave it as comment. + //#define SLIDER(S,N,W,T,F,U,D,L,H) \ + // m ## N ## Text = s.Id(ID_ ## N ## _TEXT).AddSpinCtrl(_(#S " (" #U "):"), 0, H, L); \ + //vvvvv Rob had this for every slider. It's the default, so I think unnecessary for any of them. + // s.SetStyle(wxSL_HORIZONTAL); \ + // m ## N ## Widget = s.Id(ID_ ## N ## _WIDGET).AddSlider(wxT(""), 0, H, L); + //#include "Reverb.sliders.h" + mRoomSizeText = s.Id(ID_RoomSize_TEXT).AddSpinCtrl(_("Room Size (%):"), 0, 100, 0); + mRoomSizeWidget = s.Id(ID_RoomSize_WIDGET).AddSlider(wxT(""), 0, 100, 0); + + mDelayText = s.Id(ID_Delay_TEXT).AddSpinCtrl(_("Delay (ms):"), 0, 200, 0); + mDelayWidget = s.Id(ID_Delay_WIDGET).AddSlider(wxT(""), 0, 200, 0); + + mReverberanceText = s.Id(ID_Reverberance_TEXT).AddSpinCtrl(_("Reverberance (%):"), 0, 100, 0); + mReverberanceWidget = s.Id(ID_Reverberance_WIDGET).AddSlider(wxT(""), 0, 100, 0); + + mHfDampingText = s.Id(ID_HfDamping_TEXT).AddSpinCtrl(_("Damping (%):"), 0, 100, 0); + mHfDampingWidget = s.Id(ID_HfDamping_WIDGET).AddSlider(wxT(""), 0, 100, 0); + + mToneLowText = s.Id(ID_ToneLow_TEXT).AddSpinCtrl(_("Tone Low (%):"), 0, 100, 0); + mToneLowWidget = s.Id(ID_ToneLow_WIDGET).AddSlider(wxT(""), 0, 100, 0); + + mToneHighText = s.Id(ID_ToneHigh_TEXT).AddSpinCtrl(_("Tone High (%):"), 0, 100, 0); + mToneHighWidget = s.Id(ID_ToneHigh_WIDGET).AddSlider(wxT(""), 0, 100, 0); + + mWetGainText = s.Id(ID_WetGain_TEXT).AddSpinCtrl(_("Wet Gain (dB):"), 0, 10, -20); + mWetGainWidget = s.Id(ID_WetGain_WIDGET).AddSlider(wxT(""), 0, 10, -20); + + mDryGainText = s.Id(ID_DryGain_TEXT).AddSpinCtrl(_("Dry Gain (dB):"), 0, 10, -20); + mDryGainWidget = s.Id(ID_DryGain_WIDGET).AddSlider(wxT(""), 0, 10, -20); + + mStereoWidthText = s.Id(ID_StereoWidth_TEXT).AddSpinCtrl(_("Stereo Width (%):"), 0, 100, 0); + mStereoWidthWidget = s.Id(ID_StereoWidth_WIDGET).AddSlider(wxT(""), 0, 100, 0); + } + s.EndMultiColumn(); + + s.StartHorizontalLay(wxCENTER, false); + { + mWetOnlyWidget = s.Id(ID_WetOnly_WIDGET).AddCheckBox(wxT("Wet Only"), wxT("false")); + } + s.EndHorizontalLay(); + + s.StartHorizontalLay(wxCENTER); { + s.StartStatic(_("Presets:")); { + s.Id(ID_PRESETS), s.AddButton(_("Load")); + } s.EndStatic(); + s.StartStatic(_("User settings:")); { + s.StartHorizontalLay(wxCENTER); { + s.Id(ID_LOAD_SETTINGS), s.AddButton(_("Load")); + s.Id(ID_SAVE_SETTINGS), s.AddButton(_("Save")); + s.Id(ID_RENAME_SETTINGS), s.AddButton(_("Rename")); + } s.EndHorizontalLay(); + } s.EndStatic(); + } s.EndHorizontalLay(); + return; +} + +bool ReverbDialogue::TransferDataToWindow() +{ + mRoomSizeWidget->SetValue(int(mParams.mRoomSize)); + mRoomSizeText->SetValue(wxString::Format(wxT("%d"), int(mParams.mRoomSize))); + + mDelayWidget->SetValue(int(mParams.mDelay)); + mDelayText->SetValue(wxString::Format(wxT("%d"), int(mParams.mDelay))); + + mReverberanceWidget->SetValue(int(mParams.mReverberance)); + mReverberanceText->SetValue(wxString::Format(wxT("%d"), int(mParams.mReverberance))); + + mHfDampingWidget->SetValue(int(mParams.mHfDamping)); + mHfDampingText->SetValue(wxString::Format(wxT("%d"), int(mParams.mHfDamping))); + + mToneLowWidget->SetValue(int(mParams.mToneLow)); + mToneLowText->SetValue(wxString::Format(wxT("%d"), int(mParams.mToneLow))); + + mToneHighWidget->SetValue(int(mParams.mToneHigh)); + mToneHighText->SetValue(wxString::Format(wxT("%d"), int(mParams.mToneHigh))); + + mWetGainWidget->SetValue(int(mParams.mWetGain)); + mWetGainText->SetValue(wxString::Format(wxT("%d"), int(mParams.mWetGain))); + + mDryGainWidget->SetValue(int(mParams.mDryGain)); + mDryGainText->SetValue(wxString::Format(wxT("%d"), int(mParams.mDryGain))); + + mStereoWidthWidget->SetValue(int(mParams.mStereoWidth)); + mStereoWidthText->SetValue(wxString::Format(wxT("%d"), int(mParams.mStereoWidth))); + + mWetOnlyWidget->SetValue(int(mParams.mWetOnly)); + + return true; +} + +bool ReverbDialogue::TransferDataFromWindow() +{ + mParams.mRoomSize = mRoomSizeWidget->GetValue(); + mParams.mDelay = mDelayWidget->GetValue(); + mParams.mReverberance = mReverberanceWidget->GetValue(); + mParams.mHfDamping = mHfDampingWidget->GetValue(); + mParams.mToneLow = mToneLowWidget->GetValue(); + mParams.mToneHigh = mToneHighWidget->GetValue(); + mParams.mWetGain = mWetGainWidget->GetValue(); + mParams.mDryGain = mDryGainWidget->GetValue(); + mParams.mStereoWidth = mStereoWidthWidget->GetValue(); + mParams.mWetOnly = mWetOnlyWidget->GetValue(); + return true; +} + +void ReverbDialogue::OnRoomSizeText(wxCommandEvent & WXUNUSED(event)) +{ int val = mRoomSizeText->GetValue(); mRoomSizeWidget->SetValue(TrapLong(val, 0, 100)); } +void ReverbDialogue::OnRoomSizeWidget(wxCommandEvent & WXUNUSED(event)) +{ mRoomSizeText->SetValue(wxString::Format(wxT("%d"), mRoomSizeWidget->GetValue())); } + +void ReverbDialogue::OnDelayText(wxCommandEvent & WXUNUSED(event)) +{ int val = mDelayText->GetValue(); mDelayWidget->SetValue(TrapLong(val, 0, 200)); } +void ReverbDialogue::OnDelayWidget(wxCommandEvent & WXUNUSED(event)) +{ mDelayText->SetValue(wxString::Format(wxT("%d"), mDelayWidget->GetValue())); } + +void ReverbDialogue::OnReverberanceText(wxCommandEvent & WXUNUSED(event)) +{ int val = mReverberanceText->GetValue(); mReverberanceWidget->SetValue(TrapLong(val, 0, 100)); } +void ReverbDialogue::OnReverberanceWidget(wxCommandEvent & WXUNUSED(event)) +{ mReverberanceText->SetValue(wxString::Format(wxT("%d"), mReverberanceWidget->GetValue())); } + +void ReverbDialogue::OnHfDampingText(wxCommandEvent & WXUNUSED(event)) +{ int val = mHfDampingText->GetValue(); mHfDampingWidget->SetValue(TrapLong(val, 0, 100)); } +void ReverbDialogue::OnHfDampingWidget(wxCommandEvent & WXUNUSED(event)) +{ mHfDampingText->SetValue(wxString::Format(wxT("%d"), mHfDampingWidget->GetValue())); } + +void ReverbDialogue::OnToneLowText(wxCommandEvent & WXUNUSED(event)) +{ int val = mToneLowText->GetValue(); mToneLowWidget->SetValue(TrapLong(val, 0, 100)); } +void ReverbDialogue::OnToneLowWidget(wxCommandEvent & WXUNUSED(event)) +{ mToneLowText->SetValue(wxString::Format(wxT("%d"), mToneLowWidget->GetValue())); } + +void ReverbDialogue::OnToneHighText(wxCommandEvent & WXUNUSED(event)) +{ int val = mToneHighText->GetValue(); mToneHighWidget->SetValue(TrapLong(val, 0, 100)); } +void ReverbDialogue::OnToneHighWidget(wxCommandEvent & WXUNUSED(event)) +{ mToneHighText->SetValue(wxString::Format(wxT("%d"), mToneHighWidget->GetValue())); } + +void ReverbDialogue::OnWetGainText(wxCommandEvent & WXUNUSED(event)) +{ int val = mWetGainText->GetValue(); mWetGainWidget->SetValue(TrapLong(val, -20, 10)); } +void ReverbDialogue::OnWetGainWidget(wxCommandEvent & WXUNUSED(event)) +{ mWetGainText->SetValue(wxString::Format(wxT("%d"), mWetGainWidget->GetValue())); } + +void ReverbDialogue::OnDryGainText(wxCommandEvent & WXUNUSED(event)) +{ int val = mDryGainText->GetValue(); mDryGainWidget->SetValue(TrapLong(val, -20, 10)); } +void ReverbDialogue::OnDryGainWidget(wxCommandEvent & WXUNUSED(event)) +{ mDryGainText->SetValue(wxString::Format(wxT("%d"), mDryGainWidget->GetValue())); } + +void ReverbDialogue::OnStereoWidthText(wxCommandEvent & WXUNUSED(event)) +{ int val = mStereoWidthText->GetValue(); mStereoWidthWidget->SetValue(TrapLong(val, 0, 100)); } +void ReverbDialogue::OnStereoWidthWidget(wxCommandEvent & WXUNUSED(event)) +{ mStereoWidthText->SetValue(wxString::Format(wxT("%d"), mStereoWidthWidget->GetValue())); } + + +static int wxGetChoiceFromUser(wxWindow * parent, wxString const & message, + wxString const & caption, wxArrayString const & choices, + char * * clientData = 0, long style = wxCHOICEDLG_STYLE, + wxPoint const & pos = wxDefaultPosition) // Home-grown function +{ + wxSingleChoiceDialog d(parent, message, caption, choices, clientData, style, pos); + d.ShowModal(); + return d.GetReturnCode() == wxID_CANCEL? -1 : d.GetSelection(); +} + +void ReverbDialogue::LoadPreset(wxCommandEvent & WXUNUSED(event)) +{ + EffectReverb::Params & p = mParams; + wxString caption(_("Reverb settings")); + wxString message(_("Load preset:")); + wxArrayString choices; + choices.Add(_("Vocal I")); + choices.Add(_("Vocal II")); + choices.Add(_("Bathroom")); + choices.Add(_("Small Room Bright")); + choices.Add(_("Small Room Dark")); + choices.Add(_("Medium Room")); + choices.Add(_("Large Room")); + choices.Add(_("Church Hall")); + choices.Add(_("Cathedral")); + int i(wxGetChoiceFromUser(this, message, caption, choices)); + switch (i) { + case 0: p.mRoomSize=70; p.mHfDamping=99; p.mDelay=20; p.mReverberance=40; p.mToneLow=100; p.mToneHigh=50 ; p.mWetGain=-12; p.mDryGain=0 ; p.mStereoWidth=70 ; break; + case 1: p.mRoomSize=50; p.mHfDamping=99; p.mDelay=0 ; p.mReverberance=50; p.mToneLow=50 ; p.mToneHigh=100; p.mWetGain=-1 ; p.mDryGain=-1 ; p.mStereoWidth=70 ; break; + case 2: p.mRoomSize=16; p.mHfDamping=0 ; p.mDelay=8 ; p.mReverberance=80; p.mToneLow=0 ; p.mToneHigh=100; p.mWetGain=-6 ; p.mDryGain=0 ; p.mStereoWidth=100; break; + case 3: p.mRoomSize=30; p.mHfDamping=50; p.mDelay=10; p.mReverberance=50; p.mToneLow=50 ; p.mToneHigh=100; p.mWetGain=-1 ; p.mDryGain=-1 ; p.mStereoWidth=100; break; + case 4: p.mRoomSize=30; p.mHfDamping=50; p.mDelay=10; p.mReverberance=50; p.mToneLow=100; p.mToneHigh=0 ; p.mWetGain=-1 ; p.mDryGain=-1 ; p.mStereoWidth=100; break; + case 5: p.mRoomSize=75; p.mHfDamping=50; p.mDelay=10; p.mReverberance=40; p.mToneLow=100; p.mToneHigh=70 ; p.mWetGain=-1 ; p.mDryGain=-1 ; p.mStereoWidth=70 ; break; + case 6: p.mRoomSize=85; p.mHfDamping=50; p.mDelay=10; p.mReverberance=40; p.mToneLow=100; p.mToneHigh=80 ; p.mWetGain=0 ; p.mDryGain=-6 ; p.mStereoWidth=90 ; break; + case 7: p.mRoomSize=90; p.mHfDamping=50; p.mDelay=32; p.mReverberance=60; p.mToneLow=100; p.mToneHigh=50 ; p.mWetGain=0 ; p.mDryGain=-12; p.mStereoWidth=100; break; + case 8: p.mRoomSize=90; p.mHfDamping=50; p.mDelay=16; p.mReverberance=90; p.mToneLow=100; p.mToneHigh=0 ; p.mWetGain=0 ; p.mDryGain=-20; p.mStereoWidth=100; break; + default: return; + } + p.mWetOnly=0; + TransferDataToWindow(); + SetTitle(choices[i]); +} + +int ReverbDialogue::ChooseSettings(wxString const & message) +{ + wxString caption(_("Reverb settings")); + wxArrayString choices; + for (int i = 0; i < 10; choices.Add(mEffect.SettingsName(i++))); + return wxGetChoiceFromUser(this, message, caption, choices); +} + +void ReverbDialogue::SaveSettings(wxCommandEvent & WXUNUSED(event)) +{ + int i(ChooseSettings(_("Save current settings as:"))); + if (i >= 0) { + EffectReverb::Params savedParams(mParams); + TransferDataFromWindow(); + mEffect.SaveSettings(i, &mParams); + mParams = savedParams; + SetTitle(mEffect.SettingsName(i)); + } +} + +void ReverbDialogue::SetTitle(wxString const & name) +{ + wxString title(_("Reverb")); + if (name != wxT("")) + title += wxT(": ") + name; + wxTopLevelWindow::SetTitle(title); +} + +void ReverbDialogue::LoadSettings(wxCommandEvent & WXUNUSED(event)) +{ + int i(ChooseSettings(_("Load settings:"))); + if (i >= 0) { + mEffect.LoadSettings(i, mParams); + TransferDataToWindow(); + SetTitle(mEffect.SettingsName(i)); + } +} + +void ReverbDialogue::RenameSettings(wxCommandEvent & WXUNUSED(event)) +{ + int i(ChooseSettings(_("Rename settings:"))); + if (i >= 0) { + wxString oldName = mEffect.SettingsName(i); + wxString newName = wxGetTextFromUser(_("Change name to:"), oldName, oldName, this); + if (newName != wxT("")) + mEffect.SaveSettings(i, 0, &newName); + } +} + +void ReverbDialogue::OnPreview(wxCommandEvent & event) +{ + if (event.GetId() == ePreviewID) { + EffectReverb::Params savedParams(mParams); + TransferDataFromWindow(); + mEffect.Preview(); + mParams = savedParams; + } + else mEffect.Preview(true); +} + diff --git a/src/effects/Reverb.h b/src/effects/Reverb.h new file mode 100644 index 000000000..13bfbd43a --- /dev/null +++ b/src/effects/Reverb.h @@ -0,0 +1,152 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + Audacity(R) is copyright (c) 1999-2013 Audacity Team. + License: GPL v2. See License.txt. + + Reverb.h + Rob Sykes, Vaughan Johnson + +**********************************************************************/ + +#ifndef __AUDACITY_EFFECT_REVERB__ +#define __AUDACITY_EFFECT_REVERB__ + +#include +#include +#include +#include + +#include "Effect.h" + +class wxSpinCtrl; +class WaveTrack; + +struct Reverb_priv_t; + +class EffectReverb : public Effect +{ +public: + EffectReverb(); + virtual ~EffectReverb() {}; + + // Base class: + wxString GetEffectName() {return _("Reverb...");} + wxString GetEffectAction() {return _("Applying Reverb");} + wxString GetEffectDescription(); // Useful only after PromptUser values have been set. + bool TransferParameters(Shuttle & shuttle); + + protected: + bool PromptUser(); + bool Process(); + + // Processing: + void Create(double rate, bool isStereo); + bool ProcessOneBlock(sampleCount len, float * const * chans); + bool ProcessOneTrack(size_t n, WaveTrack * track, WaveTrack * track2, wxString const & msg); + void Delete(); + double mCurT0, mCurT1; + Reverb_priv_t * mP; + + // Settings: + wxString SettingsPath(int settingsNumber) const; + wxString SettingsName(int settingsNumber) const; + + struct Params { + double mRoomSize; + double mDelay; + double mReverberance; + double mHfDamping; + double mToneLow; + double mToneHigh; + double mWetGain; + double mDryGain; + double mStereoWidth; + bool mWetOnly; + }; + void LoadSettings(int settingsNumber, Params & params); + void SaveSettings(int settingsNumber, Params const * params, wxString const * name = 0) const; + + Params mParams; + + friend class ReverbDialogue; +}; + +//---------------------------------------------------------------------------- +// ReverbDialogue +//---------------------------------------------------------------------------- +class ReverbDialogue : public EffectDialog +{ +public: + ReverbDialogue(EffectReverb * effect, wxWindow * parent); + virtual ~ReverbDialogue() {}; + +private: + void SetTitle(wxString const & name = wxT("")); + void PopulateOrExchange(ShuttleGui &); + bool TransferDataToWindow(); + bool TransferDataFromWindow(); + + void LoadPreset(wxCommandEvent & WXUNUSED(event)); + int ChooseSettings(wxString const & message); + void LoadSettings(wxCommandEvent & WXUNUSED(event)); + void RenameSettings(wxCommandEvent & WXUNUSED(event)); + void SaveSettings(wxCommandEvent & WXUNUSED(event)); + void OnPreview(wxCommandEvent & event); + + // event handlers and member vars + void OnRoomSizeWidget(wxCommandEvent & event); + void OnRoomSizeText(wxCommandEvent & event); + wxSlider * mRoomSizeWidget; + wxSpinCtrl * mRoomSizeText; + + void OnDelayWidget(wxCommandEvent & event); + void OnDelayText(wxCommandEvent & event); + wxSlider * mDelayWidget; + wxSpinCtrl * mDelayText; + + void OnReverberanceWidget(wxCommandEvent & event); + void OnReverberanceText(wxCommandEvent & event); + wxSlider * mReverberanceWidget; + wxSpinCtrl * mReverberanceText; + + void OnHfDampingWidget(wxCommandEvent & event); + void OnHfDampingText(wxCommandEvent & event); + wxSlider * mHfDampingWidget; + wxSpinCtrl * mHfDampingText; + + void OnToneLowWidget(wxCommandEvent & event); + void OnToneLowText(wxCommandEvent & event); + wxSlider * mToneLowWidget; + wxSpinCtrl * mToneLowText; + + void OnToneHighWidget(wxCommandEvent & event); + void OnToneHighText(wxCommandEvent & event); + wxSlider * mToneHighWidget; + wxSpinCtrl * mToneHighText; + + void OnWetGainWidget(wxCommandEvent & event); + void OnWetGainText(wxCommandEvent & event); + wxSlider * mWetGainWidget; + wxSpinCtrl * mWetGainText; + + void OnDryGainWidget(wxCommandEvent & event); + void OnDryGainText(wxCommandEvent & event); + wxSlider * mDryGainWidget; + wxSpinCtrl * mDryGainText; + + void OnStereoWidthWidget(wxCommandEvent & event); + void OnStereoWidthText(wxCommandEvent & event); + wxSlider * mStereoWidthWidget; + wxSpinCtrl * mStereoWidthText; + + wxCheckBox * mWetOnlyWidget; + + + EffectReverb & mEffect; + EffectReverb::Params & mParams; + + DECLARE_EVENT_TABLE() +}; + +#endif diff --git a/src/effects/Reverb_libSoX.h b/src/effects/Reverb_libSoX.h new file mode 100644 index 000000000..547056bbf --- /dev/null +++ b/src/effects/Reverb_libSoX.h @@ -0,0 +1,270 @@ +/********************************************************************** + + Audacity: A Digital Audio Editor + + Reverb_libSoX.h + Stereo reverberation effect from libSoX, + adapted for Audacity (audacity.sourceforge.net) + + Copyright (c) 2007-2013 robs@users.sourceforge.net + Licence: LGPL v2.1 + Filter configuration based on freeverb by Jezar Wakefield. + +**********************************************************************/ + +#include +#include +#ifdef __WXMSW__ + #define M_LN10 2.30258509299404568402 /* log_e 10 */ +#else + #include +#endif +#include +using std::min; +using std::max; + +#define array_length(a) (sizeof(a)/sizeof(a[0])) +#define dB_to_linear(x) exp((x) * M_LN10 * 0.05) +#define midi_to_freq(n) (440 * pow(2,((n)-69)/12.)) +#define FIFO_SIZE_T size_t +#define FIFO_MIN 0x4000 +#define fifo_read_ptr(f) fifo_read(f, (FIFO_SIZE_T)0, NULL) +#define lsx_zalloc(var, n) var = (float *)calloc(n, sizeof(*var)) +#define filter_advance(p) if (--(p)->ptr < (p)->buffer) (p)->ptr += (p)->size +#define filter_delete(p) free((p)->buffer) + +typedef struct { + char * data; + size_t allocation; /* Number of bytes allocated for data. */ + size_t item_size; /* Size of each item in data */ + size_t begin; /* Offset of the first byte to read. */ + size_t end; /* 1 + Offset of the last byte byte to read. */ +} fifo_t; + +static void fifo_clear(fifo_t * f) +{ + f->end = f->begin = 0; +} + +static void * fifo_reserve(fifo_t * f, FIFO_SIZE_T n) +{ + n *= f->item_size; + + if (f->begin == f->end) + fifo_clear(f); + + while (1) { + if (f->end + n <= f->allocation) { + void *p = f->data + f->end; + + f->end += n; + return p; + } + if (f->begin > FIFO_MIN) { + memmove(f->data, f->data + f->begin, f->end - f->begin); + f->end -= f->begin; + f->begin = 0; + continue; + } + f->allocation += n; + f->data = (char *)realloc(f->data, f->allocation); + } +} + +static void * fifo_write(fifo_t * f, FIFO_SIZE_T n, void const * data) +{ + void * s = fifo_reserve(f, n); + if (data) + memcpy(s, data, n * f->item_size); + return s; +} + +static void * fifo_read(fifo_t * f, FIFO_SIZE_T n, void * data) +{ + char * ret = f->data + f->begin; + n *= f->item_size; + if (n > (FIFO_SIZE_T)(f->end - f->begin)) + return NULL; + if (data) + memcpy(data, ret, (size_t)n); + f->begin += n; + return ret; +} + +static void fifo_delete(fifo_t * f) +{ + free(f->data); +} + +static void fifo_create(fifo_t * f, FIFO_SIZE_T item_size) +{ + f->item_size = item_size; + f->allocation = FIFO_MIN; + f->data = (char *)malloc(f->allocation); + fifo_clear(f); +} + +typedef struct { + size_t size; + float * buffer, * ptr; + float store; +} filter_t; + +static float comb_process(filter_t * p, /* gcc -O2 will inline this */ + float const * input, float const * feedback, float const * hf_damping) +{ + float output = *p->ptr; + p->store = output + (p->store - output) * *hf_damping; + *p->ptr = *input + p->store * *feedback; + filter_advance(p); + return output; +} + +static float allpass_process(filter_t * p, /* gcc -O2 will inline this */ + float const * input) +{ + float output = *p->ptr; + *p->ptr = *input + output * .5; + filter_advance(p); + return output - *input; +} + +typedef struct {double b0, b1, a1, i1, o1;} one_pole_t; + +static float one_pole_process(one_pole_t * p, float i0) +{ + float o0 = i0*p->b0 + p->i1*p->b1 - p->o1*p->a1; + p->i1 = i0; + return p->o1 = o0; +} + +static const size_t /* Filter delay lengths in samples (44100Hz sample-rate) */ + comb_lengths[] = {1116, 1188, 1277, 1356, 1422, 1491, 1557, 1617}, + allpass_lengths[] = {225, 341, 441, 556}, stereo_adjust = 12; + +typedef struct { + filter_t comb [array_length(comb_lengths)]; + filter_t allpass[array_length(allpass_lengths)]; + one_pole_t one_pole[2]; +} filter_array_t; + +static void filter_array_create(filter_array_t * p, double rate, + double scale, double offset, double fc_highpass, double fc_lowpass) +{ + size_t i; + double r = rate * (1 / 44100.); /* Compensate for actual sample-rate */ + + for (i = 0; i < array_length(comb_lengths); ++i, offset = -offset) + { + filter_t * pcomb = &p->comb[i]; + pcomb->size = (size_t)(scale * r * (comb_lengths[i] + stereo_adjust * offset) + .5); + pcomb->ptr = lsx_zalloc(pcomb->buffer, pcomb->size); + } + for (i = 0; i < array_length(allpass_lengths); ++i, offset = -offset) + { + filter_t * pallpass = &p->allpass[i]; + pallpass->size = (size_t)(r * (allpass_lengths[i] + stereo_adjust * offset) + .5); + pallpass->ptr = lsx_zalloc(pallpass->buffer, pallpass->size); + } + { /* EQ: highpass */ + one_pole_t * q = &p->one_pole[0]; + q->a1 = -exp(-2 * M_PI * fc_highpass / rate); + q->b0 = (1 - q->a1)/2, q->b1 = -q->b0; + } + { /* EQ: lowpass */ + one_pole_t * q = &p->one_pole[1]; + q->a1 = -exp(-2 * M_PI * fc_lowpass / rate); + q->b0 = 1 + q->a1, q->b1 = 0; + } +} + +static void filter_array_process(filter_array_t * p, + size_t length, float const * input, float * output, + float const * feedback, float const * hf_damping, float const * gain) +{ + while (length--) { + float out = 0, in = *input++; + + size_t i = array_length(comb_lengths) - 1; + do out += comb_process(p->comb + i, &in, feedback, hf_damping); + while (i--); + + i = array_length(allpass_lengths) - 1; + do out = allpass_process(p->allpass + i, &out); + while (i--); + + out = one_pole_process(&p->one_pole[0], out); + out = one_pole_process(&p->one_pole[1], out); + *output++ = out * *gain; + } +} + +static void filter_array_delete(filter_array_t * p) +{ + size_t i; + + for (i = 0; i < array_length(allpass_lengths); ++i) + filter_delete(&p->allpass[i]); + for (i = 0; i < array_length(comb_lengths); ++i) + filter_delete(&p->comb[i]); +} + +typedef struct { + float feedback; + float hf_damping; + float gain; + fifo_t input_fifo; + filter_array_t chan[2]; + float * out[2]; +} reverb_t; + +static void reverb_create(reverb_t * p, double sample_rate_Hz, + double wet_gain_dB, + double room_scale, /* % */ + double reverberance, /* % */ + double hf_damping, /* % */ + double pre_delay_ms, + double stereo_depth, + double tone_low, /* % */ + double tone_high, /* % */ + size_t buffer_size, + float * * out) +{ + size_t i, delay = pre_delay_ms / 1000 * sample_rate_Hz + .5; + double scale = room_scale / 100 * .9 + .1; + double depth = stereo_depth / 100; + double a = -1 / log(1 - /**/.3 /**/); /* Set minimum feedback */ + double b = 100 / (log(1 - /**/.98/**/) * a + 1); /* Set maximum feedback */ + double fc_highpass = midi_to_freq(72 - tone_low / 100 * 48); + double fc_lowpass = midi_to_freq(72 + tone_high/ 100 * 48); + + memset(p, 0, sizeof(*p)); + p->feedback = 1 - exp((reverberance - b) / (a * b)); + p->hf_damping = hf_damping / 100 * .3 + .2; + p->gain = dB_to_linear(wet_gain_dB) * .015; + fifo_create(&p->input_fifo, sizeof(float)); + memset(fifo_write(&p->input_fifo, delay, 0), 0, delay * sizeof(float)); + for (i = 0; i <= ceil(depth); ++i) { + filter_array_create(p->chan + i, sample_rate_Hz, scale, i * depth, fc_highpass, fc_lowpass); + out[i] = lsx_zalloc(p->out[i], buffer_size); + } +} + +static void reverb_process(reverb_t * p, size_t length) +{ + size_t i; + for (i = 0; i < 2 && p->out[i]; ++i) + filter_array_process(p->chan + i, length, (float *) fifo_read_ptr(&p->input_fifo), p->out[i], &p->feedback, &p->hf_damping, &p->gain); + fifo_read(&p->input_fifo, length, NULL); +} + +static void reverb_delete(reverb_t * p) +{ + size_t i; + for (i = 0; i < 2 && p->out[i]; ++i) { + free(p->out[i]); + filter_array_delete(p->chan + i); + } + fifo_delete(&p->input_fifo); +} + diff --git a/win/Projects/Audacity/Audacity.vcproj b/win/Projects/Audacity/Audacity.vcproj index 3d21941a3..5defba613 100644 --- a/win/Projects/Audacity/Audacity.vcproj +++ b/win/Projects/Audacity/Audacity.vcproj @@ -1299,6 +1299,18 @@ RelativePath="..\..\..\src\effects\Repeat.h" > + + + + + +