1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-26 15:23:48 +01:00

SpectrumPrefs takes SpectrogramSettings object as parameter, and...

... writes global preferences explicitly, and only when it is the default
settings object.

Also impose validation of settings when constructing from preferences.
This commit is contained in:
Paul Licameli
2015-06-13 23:26:40 -04:00
parent fa18e76e9b
commit 113edcc70a
4 changed files with 277 additions and 123 deletions

View File

@@ -15,6 +15,10 @@ Paul Licameli
#include "../Audacity.h"
#include "SpectrogramSettings.h"
#include <algorithm>
#include <wx/msgdlg.h>
#include "../FFT.h"
#include "../Prefs.h"
#include "../RealFFTf.h"
@@ -26,7 +30,7 @@ SpectrogramSettings::SpectrogramSettings()
: hFFT(0)
, window(0)
{
UpdatePrefs();
LoadPrefs();
}
SpectrogramSettings::SpectrogramSettings(const SpectrogramSettings &other)
@@ -98,49 +102,82 @@ SpectrogramSettings& SpectrogramSettings::defaults()
return instance;
}
void SpectrogramSettings::UpdatePrefs()
bool SpectrogramSettings::Validate(bool quiet)
{
bool destroy = false;
if (!quiet &&
maxFreq < 100) {
wxMessageBox(_("Maximum frequency must be 100 Hz or above"));
return false;
}
else
maxFreq = std::max(100, maxFreq);
if (!quiet &&
minFreq < 0) {
wxMessageBox(_("Minimum frequency must be at least 0 Hz"));
return false;
}
else
minFreq = std::max(0, minFreq);
if (!quiet &&
maxFreq <= minFreq) {
wxMessageBox(_("Minimum frequency must be less than maximum frequency"));
return false;
}
else
maxFreq = std::max(1 + minFreq, maxFreq);
if (!quiet &&
range <= 0) {
wxMessageBox(_("The range must be at least 1 dB"));
return false;
}
else
range = std::max(1, range);
if (!quiet &&
frequencyGain < 0) {
wxMessageBox(_("The frequency gain cannot be negative"));
return false;
}
else if (!quiet &&
frequencyGain > 60) {
wxMessageBox(_("The frequency gain must be no more than 60 dB/dec"));
return false;
}
else
frequencyGain =
std::max(0, std::min(60, frequencyGain));
// The rest are controlled by drop-down menus so they can't go wrong
// in the Preferences dialog, but we also come here after reading fom saved
// preference files, which could be or from future versions. Validate quietly.
windowType =
std::max(0, std::min(NumWindowFuncs() - 1, windowType));
ConvertToEnumeratedWindowSizes();
ConvertToActualWindowSizes();
return true;
}
void SpectrogramSettings::LoadPrefs()
{
minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), -1L);
maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);
// These preferences are not written anywhere in the program as of now,
// but I keep this legacy here. Who knows, someone might edit prefs files
// directly. PRL
logMaxFreq = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), -1);
if (logMaxFreq < 0)
logMaxFreq = maxFreq;
logMinFreq = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), -1);
if (logMinFreq < 0)
logMinFreq = minFreq;
if (logMinFreq < 1)
logMinFreq = 1;
range = gPrefs->Read(wxT("/Spectrum/Range"), 80L);
gain = gPrefs->Read(wxT("/Spectrum/Gain"), 20L);
frequencyGain = gPrefs->Read(wxT("/Spectrum/FrequencyGain"), 0L);
const int newWindowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256);
if (newWindowSize != windowSize) {
destroy = true;
windowSize = newWindowSize;
}
windowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256);
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
const int newZeroPaddingFactor = gPrefs->Read(wxT("/Spectrum/ZeroPaddingFactor"), 1);
if (newZeroPaddingFactor != zeroPaddingFactor) {
destroy = true;
zeroPaddingFactor = newZeroPaddingFactor;
}
zeroPaddingFactor = gPrefs->Read(wxT("/Spectrum/ZeroPaddingFactor"), 1);
#endif
int newWindowType;
gPrefs->Read(wxT("/Spectrum/WindowType"), &newWindowType, 3);
if (newWindowType != windowType) {
destroy = true;
windowType = newWindowType;
}
gPrefs->Read(wxT("/Spectrum/WindowType"), &windowType, eWinFuncHanning);
isGrayscale = (gPrefs->Read(wxT("/Spectrum/Grayscale"), 0L) != 0);
@@ -155,7 +192,63 @@ void SpectrogramSettings::UpdatePrefs()
findNotesQuantize = (gPrefs->Read(wxT("/Spectrum/FindNotesQuantize"), 0L) != 0);
#endif //EXPERIMENTAL_FIND_NOTES
if (destroy)
// Enforce legal values
Validate(true);
// These preferences are not written anywhere in the program as of now,
// but I keep this legacy here. Who knows, someone might edit prefs files
// directly. PRL
logMinFreq = gPrefs->Read(wxT("/SpectrumLog/MinFreq"), -1);
if (logMinFreq < 0)
logMinFreq = minFreq;
if (logMinFreq < 1)
logMinFreq = 1;
logMaxFreq = gPrefs->Read(wxT("/SpectrumLog/MaxFreq"), -1);
if (logMaxFreq < 0)
logMaxFreq = maxFreq;
logMaxFreq =
std::max(logMinFreq + 1, logMaxFreq);
InvalidateCaches();
}
void SpectrogramSettings::SavePrefs()
{
gPrefs->Write(wxT("/Spectrum/MinFreq"), minFreq);
gPrefs->Write(wxT("/Spectrum/MaxFreq"), maxFreq);
// Nothing wrote these. They only varied from the linear scale bounds in-session. -- PRL
// gPrefs->Write(wxT("/SpectrumLog/MaxFreq"), logMinFreq);
// gPrefs->Write(wxT("/SpectrumLog/MinFreq"), logMaxFreq);
gPrefs->Write(wxT("/Spectrum/Range"), range);
gPrefs->Write(wxT("/Spectrum/Gain"), gain);
gPrefs->Write(wxT("/Spectrum/FrequencyGain"), frequencyGain);
gPrefs->Write(wxT("/Spectrum/FFTSize"), windowSize);
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
gPrefs->Write(wxT("/Spectrum/ZeroPaddingFactor"), zeroPaddingFactor);
#endif
gPrefs->Write(wxT("/Spectrum/WindowType"), windowType);
gPrefs->Write(wxT("/Spectrum/Grayscale"), isGrayscale);
#ifdef EXPERIMENTAL_FFT_Y_GRID
gPrefs->Write(wxT("/Spectrum/FFTYGrid"), fftYGrid);
#endif //EXPERIMENTAL_FFT_Y_GRID
#ifdef EXPERIMENTAL_FIND_NOTES
gPrefs->Write(wxT("/Spectrum/FFTFindNotes"), fftFindNotes);
gPrefs->Write(wxT("/Spectrum/FindNotesMinA"), findNotesMinA);
gPrefs->Write(wxT("/Spectrum/FindNotesN"), numberOfMaxima);
gPrefs->Write(wxT("/Spectrum/FindNotesQuantize"), findNotesQuantize);
#endif //EXPERIMENTAL_FIND_NOTES
}
void SpectrogramSettings::InvalidateCaches()
{
DestroyWindows();
}
@@ -257,6 +350,38 @@ void SpectrogramSettings::CacheWindows() const
#endif // EXPERIMENTAL_USE_REALFFTF
}
void SpectrogramSettings::ConvertToEnumeratedWindowSizes()
{
unsigned size;
int logarithm;
logarithm = -LogMinWindowSize;
size = unsigned(windowSize);
while (size > 1)
size >>= 1, ++logarithm;
windowSize = std::max(0, std::min(NumWindowSizes - 1, logarithm));
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
// Choices for zero padding begin at 1
logarithm = 0;
size = unsigned(zeroPaddingFactor);
while (zeroPaddingFactor > 1)
zeroPaddingFactor >>= 1, ++logarithm;
zeroPaddingFactor = std::max(0,
std::min(LogMaxWindowSize - (windowSize + LogMinWindowSize),
logarithm
));
#endif
}
void SpectrogramSettings::ConvertToActualWindowSizes()
{
windowSize = 1 << (windowSize + LogMinWindowSize);
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
zeroPaddingFactor = 1 << zeroPaddingFactor;
#endif
}
int SpectrogramSettings::GetMinFreq(double rate) const
{
const int top = lrint(rate / 2.);

View File

@@ -14,19 +14,39 @@ Paul Licameli
#include "../Experimental.h"
struct FFTParam;
class SpectrumPrefs;
class SpectrogramSettings
{
friend class SpectrumPrefs;
public:
enum {
LogMinWindowSize = 3,
LogMaxWindowSize = 15,
NumWindowSizes = LogMaxWindowSize - LogMinWindowSize + 1,
};
static SpectrogramSettings &defaults();
SpectrogramSettings();
SpectrogramSettings(const SpectrogramSettings &other);
SpectrogramSettings& operator= (const SpectrogramSettings &other);
~SpectrogramSettings();
void UpdatePrefs();
bool IsDefault() const
{
return this == &defaults();
}
bool Validate(bool quiet);
void LoadPrefs();
void SavePrefs();
void InvalidateCaches();
void DestroyWindows();
void CacheWindows() const;
void ConvertToEnumeratedWindowSizes();
void ConvertToActualWindowSizes();
private:
int minFreq;
@@ -78,4 +98,5 @@ public:
mutable float *window;
#endif
};
#endif

View File

@@ -21,26 +21,30 @@
#include <wx/intl.h>
#include <wx/msgdlg.h>
#include "../Prefs.h"
#include "../FFT.h"
#include "../Project.h"
#include "../ShuttleGui.h"
#include "../FFT.h"
#include "../WaveTrack.h"
#include <algorithm>
SpectrumPrefs::SpectrumPrefs(wxWindow * parent)
SpectrumPrefs::SpectrumPrefs(wxWindow * parent, WaveTrack *wt)
: PrefsPanel(parent, _("Spectrograms"))
, mWt(wt)
{
int windowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 256);
Populate(windowSize);
SpectrogramSettings *const pSettings = mWt
? &mWt->GetIndependentSpectrogramSettings()
: &SpectrogramSettings::defaults();
mTempSettings = *pSettings;
mTempSettings.ConvertToEnumeratedWindowSizes();
Populate(pSettings->windowSize);
}
SpectrumPrefs::~SpectrumPrefs()
{
}
enum { maxWindowSize = 32768 };
enum {
ID_WINDOW_SIZE = 10001,
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
@@ -63,26 +67,18 @@ void SpectrumPrefs::Populate(int windowSize)
mSizeChoices.Add(wxT("8192"));
mSizeChoices.Add(wxT("16384"));
mSizeChoices.Add(_("32768 - most narrowband"));
int lastCode = 0;
for (size_t i = 0; i < mSizeChoices.GetCount(); i++) {
mSizeCodes.Add(lastCode = 1 << (i + 3));
}
wxASSERT(lastCode == maxWindowSize);
wxASSERT(mSizeChoices.size() == SpectrogramSettings::NumWindowSizes);
PopulatePaddingChoices(windowSize);
for (int i = 0; i < NumWindowFuncs(); i++) {
mTypeChoices.Add(WindowFuncName(i));
mTypeCodes.Add(i);
}
//------------------------- Main section --------------------
// Now construct the GUI itself.
// Use 'eIsCreatingFromPrefs' so that the GUI is
// initialised with values from gPrefs.
ShuttleGui S(this, eIsCreatingFromPrefs);
ShuttleGui S(this, eIsCreating);
PopulateOrExchange(S);
// ----------------------- End of main section --------------
}
@@ -107,14 +103,12 @@ void SpectrumPrefs::PopulatePaddingChoices(int windowSize)
pPaddingSizeControl->Clear();
}
mZeroPaddingCodes.Clear();
int padding = 1;
int numChoices = 0;
const int maxWindowSize = 1 << (SpectrogramSettings::LogMaxWindowSize);
while (windowSize <= maxWindowSize) {
const wxString numeral = wxString::Format(wxT("%d"), padding);
mZeroPaddingChoices.Add(numeral);
mZeroPaddingCodes.Add(padding);
if (pPaddingSizeControl)
pPaddingSizeControl->Append(numeral);
windowSize <<= 1;
@@ -138,25 +132,19 @@ void SpectrumPrefs::PopulateOrExchange(ShuttleGui & S)
S.StartMultiColumn(2);
{
S.Id(ID_WINDOW_SIZE).TieChoice(_("Window &size:"),
wxT("/Spectrum/FFTSize"),
256,
mSizeChoices,
mSizeCodes);
mTempSettings.windowSize,
&mSizeChoices);
S.SetSizeHints(mSizeChoices);
S.TieChoice(_("Window &type:"),
wxT("/Spectrum/WindowType"),
3,
mTypeChoices,
mTypeCodes);
mTempSettings.windowType,
&mTypeChoices);
S.SetSizeHints(mTypeChoices);
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
S.Id(ID_PADDING_SIZE).TieChoice(_("&Zero padding factor") + wxString(wxT(":")),
wxT("/Spectrum/ZeroPaddingFactor"),
mZeroPaddingChoice,
mZeroPaddingChoices,
mZeroPaddingCodes);
mTempSettings.zeroPaddingFactor,
&mZeroPaddingChoices);
S.SetSizeHints(mZeroPaddingChoices);
#endif
}
@@ -170,44 +158,37 @@ void SpectrumPrefs::PopulateOrExchange(ShuttleGui & S)
{
mMinFreq =
S.TieNumericTextBox(_("Mi&nimum Frequency (Hz):"),
wxT("/Spectrum/MinFreq"),
0,
mTempSettings.minFreq,
12);
mMaxFreq =
S.TieNumericTextBox(_("Ma&ximum Frequency (Hz):"),
wxT("/Spectrum/MaxFreq"),
8000,
mTempSettings.maxFreq,
12);
mGain =
S.TieNumericTextBox(_("&Gain (dB):"),
wxT("/Spectrum/Gain"),
20,
mTempSettings.gain,
8);
mRange =
S.TieNumericTextBox(_("&Range (dB):"),
wxT("/Spectrum/Range"),
80,
mTempSettings.range,
8);
mFrequencyGain =
S.TieNumericTextBox(_("Frequency g&ain (dB/dec):"),
wxT("/Spectrum/FrequencyGain"),
0,
mTempSettings.frequencyGain,
4);
}
S.EndTwoColumn();
S.TieCheckBox(_("S&how the spectrum using grayscale colors"),
wxT("/Spectrum/Grayscale"),
false);
mTempSettings.isGrayscale);
#ifdef EXPERIMENTAL_FFT_Y_GRID
S.TieCheckBox(_("Show a grid along the &Y-axis"),
wxT("/Spectrum/FFTYGrid"),
false);
mTempSettings.fftYGrid);
#endif //EXPERIMENTAL_FFT_Y_GRID
}
S.EndStatic();
@@ -220,25 +201,21 @@ void SpectrumPrefs::PopulateOrExchange(ShuttleGui & S)
{
mFindNotesMinA =
S.TieNumericTextBox(_("Minimum Amplitude (dB):"),
wxT("/Spectrum/FindNotesMinA"),
-30L,
mTempSettings.fftFindNotes,
8);
mFindNotesN =
S.TieNumericTextBox(_("Max. Number of Notes (1..128):"),
wxT("/Spectrum/FindNotesN"),
5L,
mTempSettings.findNotesMinA,
8);
}
S.EndTwoColumn();
S.TieCheckBox(_("&Find Notes"),
wxT("/Spectrum/FFTFindNotes"),
false);
mTempSettings.numberOfMaxima);
S.TieCheckBox(_("&Quantize Notes"),
wxT("/Spectrum/FindNotesQuantize"),
false);
mTempSettings.findNotesQuantize);
}
S.EndStatic();
#endif //EXPERIMENTAL_FIND_NOTES
@@ -246,59 +223,40 @@ void SpectrumPrefs::PopulateOrExchange(ShuttleGui & S)
bool SpectrumPrefs::Validate()
{
// Do checking for whole numbers
// ToDo: use wxIntegerValidator<unsigned> when available
long maxFreq;
if (!mMaxFreq->GetValue().ToLong(&maxFreq)) {
wxMessageBox(_("The maximum frequency must be an integer"));
return false;
}
if (maxFreq < 100) {
wxMessageBox(_("Maximum frequency must be 100 Hz or above"));
return false;
}
long minFreq;
if (!mMinFreq->GetValue().ToLong(&minFreq)) {
wxMessageBox(_("The minimum frequency must be an integer"));
return false;
}
if (minFreq < 0) {
wxMessageBox(_("Minimum frequency must be at least 0 Hz"));
return false;
}
if (maxFreq < minFreq) {
wxMessageBox(_("Minimum frequency must be less than maximum frequency"));
return false;
}
long gain;
if (!mGain->GetValue().ToLong(&gain)) {
wxMessageBox(_("The gain must be an integer"));
return false;
}
long range;
if (!mRange->GetValue().ToLong(&range)) {
wxMessageBox(_("The range must be a positive integer"));
return false;
}
if (range <= 0) {
wxMessageBox(_("The range must be at least 1 dB"));
return false;
}
long frequencygain;
if (!mFrequencyGain->GetValue().ToLong(&frequencygain)) {
wxMessageBox(_("The frequency gain must be an integer"));
return false;
}
if (frequencygain < 0) {
wxMessageBox(_("The frequency gain cannot be negative"));
return false;
}
if (frequencygain > 60) {
wxMessageBox(_("The frequency gain must be no more than 60 dB/dec"));
return false;
}
#ifdef EXPERIMENTAL_FIND_NOTES
long findNotesMinA;
if (!mFindNotesMinA->GetValue().ToLong(&findNotesMinA)) {
@@ -317,15 +275,52 @@ bool SpectrumPrefs::Validate()
}
#endif //EXPERIMENTAL_FIND_NOTES
return true;
ShuttleGui S(this, eIsGettingFromDialog);
PopulateOrExchange(S);
// Delegate range checking to SpectrogramSettings class
mTempSettings.ConvertToActualWindowSizes();
const bool result = mTempSettings.Validate(false);
mTempSettings.ConvertToEnumeratedWindowSizes();
return result;
}
bool SpectrumPrefs::Apply()
{
ShuttleGui S(this, eIsSavingToPrefs);
const bool isOpenPage = this->IsShown();
WaveTrack *const partner =
mWt ? static_cast<WaveTrack*>(mWt->GetLink()) : 0;
ShuttleGui S(this, eIsGettingFromDialog);
PopulateOrExchange(S);
SpectrogramSettings::defaults().UpdatePrefs();
mTempSettings.ConvertToActualWindowSizes();
if (mWt) {
SpectrogramSettings *pSettings =
&mWt->GetIndependentSpectrogramSettings();
*pSettings = mTempSettings;
if (partner) {
pSettings = &partner->GetIndependentSpectrogramSettings();
*pSettings = mTempSettings;
}
}
else {
SpectrogramSettings *const pSettings =
&SpectrogramSettings::defaults();
*pSettings = mTempSettings;
pSettings->SavePrefs();
}
mTempSettings.ConvertToEnumeratedWindowSizes();
if (mWt && isOpenPage) {
// Future: open page will determine the track view type
/*
mWt->SetDisplay(WaveTrack::Spectrum);
if (partner)
partner->SetDisplay(WaveTrack::Spectrum);
*/
}
return true;
}
@@ -334,7 +329,8 @@ void SpectrumPrefs::OnWindowSize(wxCommandEvent &)
{
wxChoice *const pWindowSizeControl =
static_cast<wxChoice*>(wxWindow::FindWindowById(ID_WINDOW_SIZE, this));
int windowSize = 1 << (pWindowSizeControl->GetSelection() + 3);
int windowSize = 1 <<
(pWindowSizeControl->GetSelection() + SpectrogramSettings::LogMinWindowSize);
PopulatePaddingChoices(windowSize);
}
@@ -342,7 +338,12 @@ BEGIN_EVENT_TABLE(SpectrumPrefs, PrefsPanel)
EVT_CHOICE(ID_WINDOW_SIZE, SpectrumPrefs::OnWindowSize)
END_EVENT_TABLE()
SpectrumPrefsFactory::SpectrumPrefsFactory(WaveTrack *wt)
: mWt(wt)
{
}
PrefsPanel *SpectrumPrefsFactory::Create(wxWindow *parent)
{
return new SpectrumPrefs(parent);
return new SpectrumPrefs(parent, mWt);
}

View File

@@ -33,11 +33,13 @@
class wxTextCtrl;
struct FFTParam;
class ShuttleGui;
class SpectrogramSettings;
class WaveTrack;
class SpectrumPrefs:public PrefsPanel
{
public:
SpectrumPrefs(wxWindow * parent);
SpectrumPrefs(wxWindow * parent, WaveTrack *wt);
virtual ~SpectrumPrefs();
virtual bool Apply();
virtual bool Validate();
@@ -50,6 +52,8 @@ class SpectrumPrefs:public PrefsPanel
void OnWindowSize(wxCommandEvent &event);
DECLARE_EVENT_TABLE()
WaveTrack *const mWt;
wxTextCtrl *mMinFreq;
wxTextCtrl *mMaxFreq;
wxTextCtrl *mGain;
@@ -57,28 +61,31 @@ class SpectrumPrefs:public PrefsPanel
wxTextCtrl *mFrequencyGain;
wxArrayString mSizeChoices;
wxArrayInt mSizeCodes;
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
int mZeroPaddingChoice;
wxArrayString mZeroPaddingChoices;
wxArrayInt mZeroPaddingCodes;
#endif
wxArrayString mTypeChoices;
wxArrayInt mTypeCodes;
#ifdef EXPERIMENTAL_FIND_NOTES
wxTextCtrl *mFindNotesMinA;
wxTextCtrl *mFindNotesN;
#endif
SpectrogramSettings mTempSettings;
};
class SpectrumPrefsFactory : public PrefsPanelFactory
{
public:
explicit SpectrumPrefsFactory(WaveTrack *wt = 0);
virtual PrefsPanel *Create(wxWindow *parent);
private:
WaveTrack *const mWt;
};
#endif