2015-06-20 20:10:40 +00:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
|
|
|
|
SpectrogramSettings.cpp
|
|
|
|
|
|
|
|
Paul Licameli
|
|
|
|
|
|
|
|
*******************************************************************//**
|
|
|
|
|
|
|
|
\class SpectrogramSettings
|
|
|
|
\brief Spectrogram settings, either for one track or as defaults.
|
|
|
|
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
|
|
|
#include "../Audacity.h"
|
|
|
|
#include "SpectrogramSettings.h"
|
2015-06-16 16:46:55 +00:00
|
|
|
#include "../NumberScale.h"
|
2015-08-17 15:49:13 +00:00
|
|
|
#include "../TranslatableStringArray.h"
|
2015-06-14 03:26:40 +00:00
|
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
#include <wx/msgdlg.h>
|
|
|
|
|
2015-06-20 20:10:40 +00:00
|
|
|
#include "../FFT.h"
|
|
|
|
#include "../Prefs.h"
|
|
|
|
#include "../RealFFTf.h"
|
|
|
|
|
2015-06-13 16:13:55 +00:00
|
|
|
#include <cmath>
|
|
|
|
|
2015-08-31 19:50:50 +00:00
|
|
|
#include "../Experimental.h"
|
|
|
|
|
2015-06-16 15:39:26 +00:00
|
|
|
SpectrogramSettings::Globals::Globals()
|
|
|
|
{
|
|
|
|
LoadPrefs();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpectrogramSettings::Globals::SavePrefs()
|
|
|
|
{
|
|
|
|
#ifdef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
|
|
|
gPrefs->Write(wxT("/Spectrum/EnableSpectralSelection"), spectralSelection);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpectrogramSettings::Globals::LoadPrefs()
|
|
|
|
{
|
|
|
|
#ifdef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
|
|
|
spectralSelection
|
|
|
|
= (gPrefs->Read(wxT("/Spectrum/EnableSpectralSelection"), 0L) != 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
SpectrogramSettings::Globals
|
|
|
|
&SpectrogramSettings::Globals::Get()
|
|
|
|
{
|
|
|
|
static Globals instance;
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2015-06-20 20:10:40 +00:00
|
|
|
SpectrogramSettings::SpectrogramSettings()
|
|
|
|
{
|
2015-06-14 03:26:40 +00:00
|
|
|
LoadPrefs();
|
2015-06-20 20:10:40 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 14:36:17 +00:00
|
|
|
SpectrogramSettings::SpectrogramSettings(const SpectrogramSettings &other)
|
|
|
|
: minFreq(other.minFreq)
|
|
|
|
, maxFreq(other.maxFreq)
|
|
|
|
, range(other.range)
|
|
|
|
, gain(other.gain)
|
|
|
|
, frequencyGain(other.frequencyGain)
|
|
|
|
, windowType(other.windowType)
|
|
|
|
, windowSize(other.windowSize)
|
|
|
|
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
|
|
|
|
, zeroPaddingFactor(other.zeroPaddingFactor)
|
|
|
|
#endif
|
|
|
|
, isGrayscale(other.isGrayscale)
|
2015-06-16 04:55:34 +00:00
|
|
|
, scaleType(other.scaleType)
|
2015-06-16 15:39:26 +00:00
|
|
|
#ifndef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
2015-06-16 04:55:34 +00:00
|
|
|
, spectralSelection(other.spectralSelection)
|
2015-06-16 15:39:26 +00:00
|
|
|
#endif
|
2015-06-20 16:15:49 +00:00
|
|
|
, algorithm(other.algorithm)
|
2015-06-15 14:36:17 +00:00
|
|
|
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
|
|
|
, fftYGrid(other.fftYGrid)
|
|
|
|
#endif
|
|
|
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
|
|
|
, fftFindNotes(other.fftFindNotes)
|
|
|
|
, findNotesMinA(other.findNotesMinA)
|
|
|
|
, numberOfMaxima(other.numberOfMaxima)
|
|
|
|
, findNotesQuantize(other.findNotesQuantize)
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Do not copy these!
|
2016-02-26 19:43:33 +00:00
|
|
|
, hFFT{}
|
|
|
|
, window{}
|
|
|
|
, tWindow{}
|
|
|
|
, dWindow{}
|
2015-06-15 14:36:17 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
SpectrogramSettings &SpectrogramSettings::operator= (const SpectrogramSettings &other)
|
|
|
|
{
|
|
|
|
if (this != &other) {
|
|
|
|
minFreq = other.minFreq;
|
|
|
|
maxFreq = other.maxFreq;
|
|
|
|
range = other.range;
|
|
|
|
gain = other.gain;
|
|
|
|
frequencyGain = other.frequencyGain;
|
|
|
|
windowType = other.windowType;
|
|
|
|
windowSize = other.windowSize;
|
|
|
|
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
|
|
|
|
zeroPaddingFactor = other.zeroPaddingFactor;
|
|
|
|
#endif
|
|
|
|
isGrayscale = other.isGrayscale;
|
2015-06-16 04:55:34 +00:00
|
|
|
scaleType = other.scaleType;
|
2015-06-16 15:39:26 +00:00
|
|
|
#ifndef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
2015-06-16 04:55:34 +00:00
|
|
|
spectralSelection = other.spectralSelection;
|
2015-06-16 15:39:26 +00:00
|
|
|
#endif
|
2015-06-20 16:15:49 +00:00
|
|
|
algorithm = other.algorithm;
|
2015-06-15 14:36:17 +00:00
|
|
|
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
|
|
|
fftYGrid = other.fftYGrid;
|
|
|
|
#endif
|
|
|
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
|
|
|
fftFindNotes = other.fftFindNotes;
|
|
|
|
findNotesMinA = other.findNotesMinA;
|
|
|
|
numberOfMaxima = other.numberOfMaxima;
|
|
|
|
findNotesQuantize = other.findNotesQuantize;
|
|
|
|
#endif
|
|
|
|
|
2015-08-17 12:39:28 +00:00
|
|
|
// Invalidate the caches
|
|
|
|
DestroyWindows();
|
2015-06-15 14:36:17 +00:00
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2015-06-20 20:10:40 +00:00
|
|
|
SpectrogramSettings& SpectrogramSettings::defaults()
|
|
|
|
{
|
|
|
|
static SpectrogramSettings instance;
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2015-06-16 04:55:34 +00:00
|
|
|
//static
|
|
|
|
const wxArrayString &SpectrogramSettings::GetScaleNames()
|
|
|
|
{
|
2016-02-24 06:06:39 +00:00
|
|
|
class ScaleNamesArray final : public TranslatableStringArray
|
2015-08-17 15:49:13 +00:00
|
|
|
{
|
2016-02-24 06:06:47 +00:00
|
|
|
void Populate() override
|
2015-08-17 15:49:13 +00:00
|
|
|
{
|
|
|
|
// Keep in correspondence with enum SpectrogramSettings::ScaleType:
|
|
|
|
mContents.Add(_("Linear"));
|
|
|
|
mContents.Add(_("Logarithmic"));
|
|
|
|
/* i18n-hint: The name of a frequency scale in psychoacoustics */
|
|
|
|
mContents.Add(_("Mel"));
|
|
|
|
/* i18n-hint: The name of a frequency scale in psychoacoustics, named for Heinrich Barkhausen */
|
|
|
|
mContents.Add(_("Bark"));
|
|
|
|
/* i18n-hint: The name of a frequency scale in psychoacoustics, abbreviates Equivalent Rectangular Bandwidth */
|
2015-09-03 18:41:04 +00:00
|
|
|
mContents.Add(_("ERB"));
|
2015-08-31 17:16:23 +00:00
|
|
|
/* i18n-hint: Time units, that is Period = 1 / Frequency */
|
|
|
|
mContents.Add(_("Period"));
|
2015-08-17 15:49:13 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static ScaleNamesArray theArray;
|
|
|
|
return theArray.Get();
|
2015-06-16 04:55:34 +00:00
|
|
|
}
|
|
|
|
|
2015-06-20 16:15:49 +00:00
|
|
|
//static
|
|
|
|
const wxArrayString &SpectrogramSettings::GetAlgorithmNames()
|
|
|
|
{
|
2016-02-24 06:06:39 +00:00
|
|
|
class AlgorithmNamesArray final : public TranslatableStringArray
|
2015-08-17 15:49:13 +00:00
|
|
|
{
|
2016-02-24 06:06:47 +00:00
|
|
|
void Populate() override
|
2015-08-17 15:49:13 +00:00
|
|
|
{
|
|
|
|
// Keep in correspondence with enum SpectrogramSettings::Algorithm:
|
|
|
|
mContents.Add(_("Frequencies"));
|
|
|
|
/* i18n-hint: the Reassignment algorithm for spectrograms */
|
|
|
|
mContents.Add(_("Reassignment"));
|
|
|
|
/* i18n-hint: EAC abbreviates "Enhanced Autocorrelation" */
|
|
|
|
mContents.Add(_("Pitch (EAC)"));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static AlgorithmNamesArray theArray;
|
|
|
|
return theArray.Get();
|
2015-06-20 16:15:49 +00:00
|
|
|
}
|
|
|
|
|
2015-06-14 03:26:40 +00:00
|
|
|
bool SpectrogramSettings::Validate(bool quiet)
|
2015-06-20 20:10:40 +00:00
|
|
|
{
|
2015-06-14 03:26:40 +00:00
|
|
|
if (!quiet &&
|
|
|
|
maxFreq < 100) {
|
|
|
|
wxMessageBox(_("Maximum frequency must be 100 Hz or above"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
maxFreq = std::max(100, maxFreq);
|
2015-06-20 20:10:40 +00:00
|
|
|
|
2015-06-14 03:26:40 +00:00
|
|
|
if (!quiet &&
|
|
|
|
minFreq < 0) {
|
|
|
|
wxMessageBox(_("Minimum frequency must be at least 0 Hz"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
minFreq = std::max(0, minFreq);
|
2015-06-20 20:10:40 +00:00
|
|
|
|
2015-06-14 03:26:40 +00:00
|
|
|
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));
|
2015-06-16 04:55:34 +00:00
|
|
|
scaleType =
|
|
|
|
ScaleType(std::max(0,
|
2016-09-20 12:26:42 +00:00
|
|
|
std::min((int)(SpectrogramSettings::stNumScaleTypes) - 1,
|
|
|
|
(int)(scaleType))));
|
2015-06-20 16:15:49 +00:00
|
|
|
algorithm = Algorithm(
|
2016-09-20 12:26:42 +00:00
|
|
|
std::max(0, std::min((int)(algNumAlgorithms) - 1, (int)(algorithm)))
|
2015-06-20 16:15:49 +00:00
|
|
|
);
|
2015-06-14 03:26:40 +00:00
|
|
|
ConvertToEnumeratedWindowSizes();
|
|
|
|
ConvertToActualWindowSizes();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpectrogramSettings::LoadPrefs()
|
|
|
|
{
|
|
|
|
minFreq = gPrefs->Read(wxT("/Spectrum/MinFreq"), 0L);
|
|
|
|
|
|
|
|
maxFreq = gPrefs->Read(wxT("/Spectrum/MaxFreq"), 8000L);
|
2015-06-20 20:10:40 +00:00
|
|
|
|
|
|
|
range = gPrefs->Read(wxT("/Spectrum/Range"), 80L);
|
|
|
|
gain = gPrefs->Read(wxT("/Spectrum/Gain"), 20L);
|
|
|
|
frequencyGain = gPrefs->Read(wxT("/Spectrum/FrequencyGain"), 0L);
|
|
|
|
|
2017-06-26 21:48:54 +00:00
|
|
|
windowSize = gPrefs->Read(wxT("/Spectrum/FFTSize"), 1024);
|
2015-06-20 20:10:40 +00:00
|
|
|
|
|
|
|
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
|
2015-06-14 03:26:40 +00:00
|
|
|
zeroPaddingFactor = gPrefs->Read(wxT("/Spectrum/ZeroPaddingFactor"), 1);
|
2015-06-20 20:10:40 +00:00
|
|
|
#endif
|
|
|
|
|
2015-06-14 03:26:40 +00:00
|
|
|
gPrefs->Read(wxT("/Spectrum/WindowType"), &windowType, eWinFuncHanning);
|
2015-06-20 20:10:40 +00:00
|
|
|
|
|
|
|
isGrayscale = (gPrefs->Read(wxT("/Spectrum/Grayscale"), 0L) != 0);
|
|
|
|
|
2015-06-16 04:55:34 +00:00
|
|
|
scaleType = ScaleType(gPrefs->Read(wxT("/Spectrum/ScaleType"), 0L));
|
2015-06-16 15:39:26 +00:00
|
|
|
|
|
|
|
#ifndef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
2015-06-16 04:55:34 +00:00
|
|
|
spectralSelection = (gPrefs->Read(wxT("/Spectrum/EnableSpectralSelection"), 0L) != 0);
|
2015-06-16 15:39:26 +00:00
|
|
|
#endif
|
2015-06-16 04:55:34 +00:00
|
|
|
|
2015-06-20 16:15:49 +00:00
|
|
|
algorithm = Algorithm(gPrefs->Read(wxT("/Spectrum/Algorithm"), 0L));
|
|
|
|
|
2015-06-20 20:10:40 +00:00
|
|
|
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
|
|
|
fftYGrid = (gPrefs->Read(wxT("/Spectrum/FFTYGrid"), 0L) != 0);
|
|
|
|
#endif //EXPERIMENTAL_FFT_Y_GRID
|
|
|
|
|
|
|
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
|
|
|
fftFindNotes = (gPrefs->Read(wxT("/Spectrum/FFTFindNotes"), 0L) != 0);
|
|
|
|
findNotesMinA = gPrefs->Read(wxT("/Spectrum/FindNotesMinA"), -30.0);
|
|
|
|
numberOfMaxima = gPrefs->Read(wxT("/Spectrum/FindNotesN"), 5L);
|
|
|
|
findNotesQuantize = (gPrefs->Read(wxT("/Spectrum/FindNotesQuantize"), 0L) != 0);
|
|
|
|
#endif //EXPERIMENTAL_FIND_NOTES
|
|
|
|
|
2015-06-14 03:26:40 +00:00
|
|
|
// Enforce legal values
|
|
|
|
Validate(true);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2015-07-28 20:47:19 +00:00
|
|
|
gPrefs->Write(wxT("/Spectrum/ScaleType"), (int) scaleType);
|
2015-06-16 15:39:26 +00:00
|
|
|
|
|
|
|
#ifndef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
2015-06-16 04:55:34 +00:00
|
|
|
gPrefs->Write(wxT("/Spectrum/EnableSpectralSelection"), spectralSelection);
|
2015-06-16 15:39:26 +00:00
|
|
|
#endif
|
2015-06-16 04:55:34 +00:00
|
|
|
|
2015-07-28 20:47:19 +00:00
|
|
|
gPrefs->Write(wxT("/Spectrum/Algorithm"), (int) algorithm);
|
2015-06-20 16:15:49 +00:00
|
|
|
|
2015-06-14 03:26:40 +00:00
|
|
|
#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();
|
2015-06-20 20:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SpectrogramSettings::~SpectrogramSettings()
|
|
|
|
{
|
|
|
|
DestroyWindows();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpectrogramSettings::DestroyWindows()
|
|
|
|
{
|
2016-08-13 15:02:35 +00:00
|
|
|
hFFT.reset();
|
2016-04-14 16:17:59 +00:00
|
|
|
window.reset();
|
|
|
|
dWindow.reset();
|
|
|
|
tWindow.reset();
|
2015-06-20 20:10:40 +00:00
|
|
|
}
|
|
|
|
|
2015-06-15 14:36:17 +00:00
|
|
|
|
2015-06-20 20:10:40 +00:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
enum { WINDOW, TWINDOW, DWINDOW };
|
|
|
|
void RecreateWindow(
|
2016-04-14 16:17:59 +00:00
|
|
|
Floats &window, int which, size_t fftLen,
|
2016-09-08 18:28:34 +00:00
|
|
|
size_t padding, int windowType, size_t windowSize, double &scale)
|
2015-06-20 20:10:40 +00:00
|
|
|
{
|
|
|
|
// Create the requested window function
|
2016-04-14 16:17:59 +00:00
|
|
|
window = Floats{ fftLen };
|
2017-12-08 11:26:09 +00:00
|
|
|
size_t ii;
|
2015-06-20 20:10:40 +00:00
|
|
|
|
2015-05-31 18:58:10 +00:00
|
|
|
const bool extra = padding > 0;
|
2015-06-20 20:10:40 +00:00
|
|
|
wxASSERT(windowSize % 2 == 0);
|
2015-05-31 18:58:10 +00:00
|
|
|
if (extra)
|
|
|
|
// For windows that do not go to 0 at the edges, this improves symmetry
|
|
|
|
++windowSize;
|
2017-12-08 11:26:09 +00:00
|
|
|
const size_t endOfWindow = padding + windowSize;
|
2015-06-20 20:10:40 +00:00
|
|
|
// Left and right padding
|
|
|
|
for (ii = 0; ii < padding; ++ii) {
|
|
|
|
window[ii] = 0.0;
|
|
|
|
window[fftLen - ii - 1] = 0.0;
|
|
|
|
}
|
|
|
|
// Default rectangular window in the middle
|
|
|
|
for (; ii < endOfWindow; ++ii)
|
|
|
|
window[ii] = 1.0;
|
|
|
|
// Overwrite middle as needed
|
|
|
|
switch (which) {
|
|
|
|
case WINDOW:
|
2016-04-14 16:17:59 +00:00
|
|
|
NewWindowFunc(windowType, windowSize, extra, window.get() + padding);
|
2015-06-20 20:10:40 +00:00
|
|
|
break;
|
2015-05-31 18:58:10 +00:00
|
|
|
case TWINDOW:
|
2016-04-14 16:17:59 +00:00
|
|
|
NewWindowFunc(windowType, windowSize, extra, window.get() + padding);
|
2017-12-08 11:26:09 +00:00
|
|
|
for (int ii = padding, multiplier = -(int)windowSize / 2; ii < (int)endOfWindow; ++ii, ++multiplier)
|
2015-06-20 20:10:40 +00:00
|
|
|
window[ii] *= multiplier;
|
|
|
|
break;
|
|
|
|
case DWINDOW:
|
2016-04-14 16:17:59 +00:00
|
|
|
DerivativeOfWindowFunc(windowType, windowSize, extra, window.get() + padding);
|
2015-06-20 20:10:40 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
wxASSERT(false);
|
|
|
|
}
|
|
|
|
// Scale the window function to give 0dB spectrum for 0dB sine tone
|
|
|
|
if (which == WINDOW) {
|
|
|
|
scale = 0.0;
|
|
|
|
for (ii = padding; ii < endOfWindow; ++ii)
|
|
|
|
scale += window[ii];
|
|
|
|
if (scale > 0)
|
|
|
|
scale = 2.0 / scale;
|
|
|
|
}
|
|
|
|
for (ii = padding; ii < endOfWindow; ++ii)
|
|
|
|
window[ii] *= scale;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpectrogramSettings::CacheWindows() const
|
|
|
|
{
|
|
|
|
if (hFFT == NULL || window == NULL) {
|
|
|
|
|
|
|
|
double scale;
|
2016-09-08 18:28:34 +00:00
|
|
|
const auto fftLen = WindowSize() * ZeroPaddingFactor();
|
2017-02-13 20:29:35 +00:00
|
|
|
const auto padding = (WindowSize() * (zeroPaddingFactor - 1)) / 2;
|
2015-06-20 20:10:40 +00:00
|
|
|
|
2016-08-13 15:02:35 +00:00
|
|
|
hFFT = GetFFT(fftLen);
|
2015-06-20 20:10:40 +00:00
|
|
|
RecreateWindow(window, WINDOW, fftLen, padding, windowType, windowSize, scale);
|
2015-05-31 18:58:10 +00:00
|
|
|
if (algorithm == algReassignment) {
|
|
|
|
RecreateWindow(tWindow, TWINDOW, fftLen, padding, windowType, windowSize, scale);
|
|
|
|
RecreateWindow(dWindow, DWINDOW, fftLen, padding, windowType, windowSize, scale);
|
|
|
|
}
|
2015-06-20 20:10:40 +00:00
|
|
|
}
|
|
|
|
}
|
2015-06-20 16:39:14 +00:00
|
|
|
|
2015-06-14 03:26:40 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2017-02-10 18:56:31 +00:00
|
|
|
float SpectrogramSettings::findBin( float frequency, float binUnit ) const
|
|
|
|
{
|
|
|
|
float linearBin = frequency / binUnit;
|
|
|
|
if (linearBin < 0)
|
|
|
|
return -1;
|
|
|
|
else
|
|
|
|
return linearBin;
|
|
|
|
}
|
|
|
|
|
2016-09-08 18:28:34 +00:00
|
|
|
size_t SpectrogramSettings::GetFFTLength() const
|
2015-06-20 16:39:14 +00:00
|
|
|
{
|
|
|
|
return windowSize
|
|
|
|
#ifdef EXPERIMENTAL_ZERO_PADDED_SPECTROGRAMS
|
2015-05-31 18:58:20 +00:00
|
|
|
* ((algorithm != algPitchEAC) ? zeroPaddingFactor : 1);
|
2015-06-20 16:39:14 +00:00
|
|
|
#endif
|
|
|
|
;
|
|
|
|
}
|
2015-06-16 15:39:26 +00:00
|
|
|
|
2017-02-13 20:29:35 +00:00
|
|
|
size_t SpectrogramSettings::NBins() const
|
|
|
|
{
|
|
|
|
// Omit the Nyquist frequency bin
|
|
|
|
return GetFFTLength() / 2;
|
|
|
|
}
|
|
|
|
|
2017-02-10 18:56:31 +00:00
|
|
|
NumberScale SpectrogramSettings::GetScale( float minFreq, float maxFreq ) const
|
2015-06-16 16:46:55 +00:00
|
|
|
{
|
|
|
|
NumberScaleType type = nstLinear;
|
|
|
|
|
|
|
|
// Don't assume the correspondence of the enums will remain direct in the future.
|
|
|
|
// Do this switch.
|
|
|
|
switch (scaleType) {
|
|
|
|
default:
|
|
|
|
wxASSERT(false);
|
|
|
|
case stLinear:
|
|
|
|
type = nstLinear; break;
|
|
|
|
case stLogarithmic:
|
|
|
|
type = nstLogarithmic; break;
|
2015-06-16 18:10:21 +00:00
|
|
|
case stMel:
|
|
|
|
type = nstMel; break;
|
|
|
|
case stBark:
|
|
|
|
type = nstBark; break;
|
|
|
|
case stErb:
|
|
|
|
type = nstErb; break;
|
2015-09-06 22:09:16 +00:00
|
|
|
case stPeriod:
|
|
|
|
type = nstPeriod; break;
|
2015-06-16 16:46:55 +00:00
|
|
|
}
|
|
|
|
|
2017-02-10 18:56:31 +00:00
|
|
|
return NumberScale(type, minFreq, maxFreq);
|
2015-06-16 16:46:55 +00:00
|
|
|
}
|
|
|
|
|
2015-06-16 15:39:26 +00:00
|
|
|
bool SpectrogramSettings::SpectralSelectionEnabled() const
|
|
|
|
{
|
|
|
|
#ifdef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
|
|
|
return Globals::Get().spectralSelection;
|
|
|
|
#else
|
|
|
|
return spectralSelection;
|
|
|
|
#endif
|
|
|
|
}
|