mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-15 15:49:36 +02:00
Rob Sykes's patch (considerably modified because of MSVC preprocessor problems) to add sox-based reverb
This commit is contained in:
parent
70ebaa3cc0
commit
5555c2db62
@ -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 \
|
||||
|
@ -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 );
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
577
src/effects/Reverb.cpp
Normal file
577
src/effects/Reverb.cpp
Normal file
@ -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 <wx/button.h>
|
||||
#include <wx/choicdlg.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/spinctrl.h>
|
||||
#include <wx/textdlg.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
152
src/effects/Reverb.h
Normal file
152
src/effects/Reverb.h
Normal file
@ -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 <wx/checkbox.h>
|
||||
#include <wx/dialog.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/slider.h>
|
||||
|
||||
#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
|
270
src/effects/Reverb_libSoX.h
Normal file
270
src/effects/Reverb_libSoX.h
Normal file
@ -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 <cstring>
|
||||
#include <cstdlib>
|
||||
#ifdef __WXMSW__
|
||||
#define M_LN10 2.30258509299404568402 /* log_e 10 */
|
||||
#else
|
||||
#include <cmath>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
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);
|
||||
}
|
||||
|
@ -1299,6 +1299,18 @@
|
||||
RelativePath="..\..\..\src\effects\Repeat.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\effects\Reverb.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\effects\Reverb.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\effects\Reverb_libSoX.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\..\src\effects\Reverse.cpp"
|
||||
>
|
||||
|
Loading…
x
Reference in New Issue
Block a user