mirror of
https://github.com/cookiengineer/audacity
synced 2025-08-01 08:29:27 +02:00
Non-linear effects now process tracks before mixing. This will be slower when multiple tracks are selected but the preview should now match the applied effect. SetLinearEffectFlag(true) allows linear effects to preview more quickly when multiple tracks selected, by pre-mixing selected tracks. Simple generators like Tone and Noise may be marked as 'linear' so that they only preview a few seconds. Generators that vary over time (such as Chirp) must use the full duration that is set. As this currently requires calculating the full duration, preview for 'non-linear' generators are not limited to the preview length.
271 lines
6.2 KiB
C++
271 lines
6.2 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
Noise.cpp
|
|
|
|
Dominic Mazzoni
|
|
|
|
*******************************************************************//**
|
|
|
|
\class EffectNoise
|
|
\brief An effect to add white noise.
|
|
|
|
*//*******************************************************************/
|
|
|
|
#include "../Audacity.h"
|
|
|
|
#include <math.h>
|
|
|
|
#include <wx/choice.h>
|
|
#include <wx/intl.h>
|
|
#include <wx/textctrl.h>
|
|
#include <wx/valgen.h>
|
|
|
|
#include "../Prefs.h"
|
|
#include "../widgets/valnum.h"
|
|
|
|
#include "Noise.h"
|
|
|
|
enum kTypes
|
|
{
|
|
kWhite,
|
|
kPink,
|
|
kBrownian,
|
|
kNumTypes
|
|
};
|
|
|
|
static const wxChar *kTypeStrings[kNumTypes] =
|
|
{
|
|
XO("White"),
|
|
XO("Pink"),
|
|
XO("Brownian")
|
|
};
|
|
|
|
// Define keys, defaults, minimums, and maximums for the effect parameters
|
|
//
|
|
// Name Type Key Def Min Max Scale
|
|
Param( Type, int, XO("Type"), kWhite, 0, kNumTypes - 1, 1 );
|
|
Param( Amp, double, XO("Amplitude"), 0.8, 0.0, 1.0, 1 );
|
|
|
|
//
|
|
// EffectNoise
|
|
//
|
|
|
|
EffectNoise::EffectNoise()
|
|
{
|
|
mType = DEF_Type;
|
|
mAmp = DEF_Amp;
|
|
|
|
SetLinearEffectFlag(true);
|
|
|
|
y = z = buf0 = buf1 = buf2 = buf3 = buf4 = buf5 = buf6 = 0;
|
|
}
|
|
|
|
EffectNoise::~EffectNoise()
|
|
{
|
|
}
|
|
|
|
// IdentInterface implementation
|
|
|
|
wxString EffectNoise::GetSymbol()
|
|
{
|
|
return NOISE_PLUGIN_SYMBOL;
|
|
}
|
|
|
|
wxString EffectNoise::GetDescription()
|
|
{
|
|
return XO("Generates one of three different types of noise");
|
|
}
|
|
|
|
// EffectIdentInterface implementation
|
|
|
|
EffectType EffectNoise::GetType()
|
|
{
|
|
return EffectTypeGenerate;
|
|
}
|
|
|
|
// EffectClientInterface implementation
|
|
|
|
int EffectNoise::GetAudioOutCount()
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
sampleCount EffectNoise::ProcessBlock(float **WXUNUSED(inbuf), float **outbuf, sampleCount size)
|
|
{
|
|
float *buffer = outbuf[0];
|
|
|
|
float white;
|
|
float amplitude;
|
|
float div = ((float) RAND_MAX) / 2.0f;
|
|
|
|
switch (mType)
|
|
{
|
|
default:
|
|
case kWhite: // white
|
|
for (sampleCount i = 0; i < size; i++)
|
|
{
|
|
buffer[i] = mAmp * ((rand() / div) - 1.0f);
|
|
}
|
|
break;
|
|
|
|
case kPink: // pink
|
|
// based on Paul Kellet's "instrumentation grade" algorithm.
|
|
|
|
// 0.129f is an experimental normalization factor.
|
|
amplitude = mAmp * 0.129f;
|
|
for (sampleCount i = 0; i < size; i++)
|
|
{
|
|
white = (rand() / div) - 1.0f;
|
|
buf0 = 0.99886f * buf0 + 0.0555179f * white;
|
|
buf1 = 0.99332f * buf1 + 0.0750759f * white;
|
|
buf2 = 0.96900f * buf2 + 0.1538520f * white;
|
|
buf3 = 0.86650f * buf3 + 0.3104856f * white;
|
|
buf4 = 0.55000f * buf4 + 0.5329522f * white;
|
|
buf5 = -0.7616f * buf5 - 0.0168980f * white;
|
|
buffer[i] = amplitude *
|
|
(buf0 + buf1 + buf2 + buf3 + buf4 + buf5 + buf6 + white * 0.5362);
|
|
buf6 = white * 0.115926;
|
|
}
|
|
break;
|
|
|
|
case kBrownian: // Brownian
|
|
//float leakage=0.997; // experimental value at 44.1kHz
|
|
//double scaling = 0.05; // experimental value at 44.1kHz
|
|
// min and max protect against instability at extreme sample rates.
|
|
float leakage = ((mSampleRate - 144.0) / mSampleRate < 0.9999)
|
|
? (mSampleRate - 144.0) / mSampleRate
|
|
: 0.9999f;
|
|
|
|
float scaling = (9.0 / sqrt(mSampleRate) > 0.01)
|
|
? 9.0 / sqrt(mSampleRate)
|
|
: 0.01f;
|
|
|
|
for (sampleCount i = 0; i < size; i++)
|
|
{
|
|
white = (rand() / div) - 1.0f;
|
|
z = leakage * y + white * scaling;
|
|
y = fabs(z) > 1.0
|
|
? leakage * y - white * scaling
|
|
: z;
|
|
buffer[i] = mAmp * y;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
bool EffectNoise::GetAutomationParameters(EffectAutomationParameters & parms)
|
|
{
|
|
parms.Write(KEY_Type, kTypeStrings[mType]);
|
|
parms.Write(KEY_Amp, mAmp);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EffectNoise::SetAutomationParameters(EffectAutomationParameters & parms)
|
|
{
|
|
ReadAndVerifyEnum(Type, wxArrayString(kNumTypes, kTypeStrings));
|
|
ReadAndVerifyDouble(Amp);
|
|
|
|
mType = Type;
|
|
mAmp = Amp;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Effect implementation
|
|
|
|
bool EffectNoise::Startup()
|
|
{
|
|
wxString base = wxT("/Effects/Noise/");
|
|
|
|
// Migrate settings from 2.1.0 or before
|
|
|
|
// Already migrated, so bail
|
|
if (gPrefs->Exists(base + wxT("Migrated")))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// Load the old "current" settings
|
|
if (gPrefs->Exists(base))
|
|
{
|
|
gPrefs->Read(base + wxT("Type"), &mType, 0L);
|
|
gPrefs->Read(base + wxT("Amplitude"), &mAmp, 0.8f);
|
|
|
|
SaveUserPreset(GetCurrentSettingsGroup());
|
|
|
|
// Do not migrate again
|
|
gPrefs->Write(base + wxT("Migrated"), true);
|
|
gPrefs->Flush();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void EffectNoise::PopulateOrExchange(ShuttleGui & S)
|
|
{
|
|
wxASSERT(kNumTypes == WXSIZEOF(kTypeStrings));
|
|
|
|
wxArrayString typeChoices;
|
|
for (int i = 0; i < kNumTypes; i++)
|
|
{
|
|
typeChoices.Add(wxGetTranslation(kTypeStrings[i]));
|
|
}
|
|
|
|
S.StartMultiColumn(2, wxCENTER);
|
|
{
|
|
S.AddChoice(_("Noise type:"), wxT(""), &typeChoices)->SetValidator(wxGenericValidator(&mType));
|
|
|
|
FloatingPointValidator<double> vldAmp(1, &mAmp, NUM_VAL_NO_TRAILING_ZEROES);
|
|
vldAmp.SetRange(MIN_Amp, MAX_Amp);
|
|
S.AddTextBox(_("Amplitude (0-1):"), wxT(""), 12)->SetValidator(vldAmp);
|
|
|
|
bool isSelection;
|
|
double duration = GetDuration(&isSelection);
|
|
|
|
S.AddPrompt(_("Duration:"));
|
|
mNoiseDurationT = new
|
|
NumericTextCtrl(NumericConverter::TIME,
|
|
S.GetParent(),
|
|
wxID_ANY,
|
|
isSelection ? _("hh:mm:ss + samples") : _("hh:mm:ss + milliseconds"),
|
|
duration,
|
|
mProjectRate,
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
true);
|
|
mNoiseDurationT->SetName(_("Duration"));
|
|
mNoiseDurationT->EnableMenu();
|
|
S.AddWindow(mNoiseDurationT, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL);
|
|
}
|
|
S.EndMultiColumn();
|
|
}
|
|
|
|
bool EffectNoise::TransferDataToWindow()
|
|
{
|
|
if (!mUIParent->TransferDataToWindow())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
mNoiseDurationT->SetValue(GetDuration());
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EffectNoise::TransferDataFromWindow()
|
|
{
|
|
if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
SetDuration(mNoiseDurationT->GetValue());
|
|
|
|
return true;
|
|
}
|