1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-17 16:40:07 +02:00

Bug1169: end the separate treatment of lin vs. log display bounds for spectrum...

... also make WaveTrack responsible for storing and validating the bounds
... also let the bounds vary per-track even though other settings are default
... also change some code names to mention "period" not "undertone"
This commit is contained in:
Paul Licameli 2015-09-06 18:09:16 -04:00
parent c86c262efa
commit b84c1b322e
8 changed files with 125 additions and 209 deletions

View File

@ -22,7 +22,7 @@ enum NumberScaleType {
nstMel,
nstBark,
nstErb,
nstUndertone,
nstPeriod,
nstNumScaleTypes,
};
@ -31,6 +31,10 @@ enum NumberScaleType {
class NumberScale
{
public:
NumberScale()
: mType(nstLinear), mValue0(0), mValue1(1), mUnit(1)
{}
NumberScale(NumberScaleType type,
float value0, float value1, float unit)
: mType(type)
@ -71,10 +75,10 @@ public:
mUnit = unit;
}
break;
case nstUndertone:
case nstPeriod:
{
mValue0 = hzToUndertone(value0);
mValue1 = hzToUndertone(value1);
mValue0 = hzToPeriod(value0);
mValue1 = hzToPeriod(value1);
mUnit = unit;
}
break;
@ -144,12 +148,12 @@ public:
return 676170.4 / (47.06538 - exp(0.08950404 * erb)) - 14678.49;
}
static inline float hzToUndertone(float hz)
static inline float hzToPeriod(float hz)
{
return -1.0 / std::max (1.0f, hz);
}
static inline float undertoneToHz(float u)
static inline float periodToHz(float u)
{
return -1.0 / u;
}
@ -170,8 +174,8 @@ public:
return barkToHz(mValue0 + pp * (mValue1 - mValue0)) / mUnit;
case nstErb:
return erbToHz(mValue0 + pp * (mValue1 - mValue0)) / mUnit;
case nstUndertone:
return undertoneToHz(mValue0 + pp * (mValue1 - mValue0)) / mUnit;
case nstPeriod:
return periodToHz(mValue0 + pp * (mValue1 - mValue0)) / mUnit;
}
}
@ -199,8 +203,8 @@ public:
return barkToHz(mValue) / mUnit;
case nstErb:
return erbToHz(mValue) / mUnit;
case nstUndertone:
return undertoneToHz(mValue) / mUnit;
case nstPeriod:
return periodToHz(mValue) / mUnit;
}
}
@ -211,7 +215,7 @@ public:
case nstMel:
case nstBark:
case nstErb:
case nstUndertone:
case nstPeriod:
mValue += mStep;
break;
case nstLogarithmic:
@ -239,7 +243,7 @@ public:
case nstMel:
case nstBark:
case nstErb:
case nstUndertone:
case nstPeriod:
return Iterator
(mType,
nPositions == 1 ? 0 : (mValue1 - mValue0) / (nPositions - 1),
@ -268,13 +272,13 @@ public:
return ((hzToBark(val * mUnit) - mValue0) / (mValue1 - mValue0));
case nstErb:
return ((hzToErb(val * mUnit) - mValue0) / (mValue1 - mValue0));
case nstUndertone:
return ((hzToUndertone(val * mUnit) - mValue0) / (mValue1 - mValue0));
case nstPeriod:
return ((hzToPeriod(val * mUnit) - mValue0) / (mValue1 - mValue0));
}
}
private:
const NumberScaleType mType;
NumberScaleType mType;
float mValue0;
float mValue1;
float mUnit;

View File

@ -831,6 +831,10 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
}
else {
wxASSERT(display == WaveTrack::Spectrum);
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
float minFreq, maxFreq;
wt->GetSpectrumBounds(&minFreq, &maxFreq);
switch (wt->GetSpectrogramSettings().scaleType) {
default:
wxASSERT(false);
@ -841,11 +845,6 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
if (rect.height < 60)
return;
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
const double rate = wt->GetRate();
const int maxFreq = settings.GetMaxFreq(rate);
const int minFreq = settings.GetMinFreq(rate);
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
@ -872,18 +871,13 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
case SpectrogramSettings::stMel:
case SpectrogramSettings::stBark:
case SpectrogramSettings::stErb:
case SpectrogramSettings::stUndertone:
case SpectrogramSettings::stPeriod:
{
// SpectrumLog
if (rect.height < 10)
return;
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
const double rate = wt->GetRate();
const int maxFreq = settings.GetLogMaxFreq(rate);
const int minFreq = settings.GetLogMinFreq(rate);
/*
draw the ruler
we will use Hz if maxFreq is < 2000, otherwise we represent kHz,
@ -897,7 +891,8 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
vruler->SetUnits(wxT(""));
vruler->SetLog(true);
NumberScale scale
(wt->GetSpectrogramSettings().GetScale(wt->GetRate(), false).Reversal());
(wt->GetSpectrogramSettings().GetScale
(minFreq, maxFreq, wt->GetRate(), false).Reversal());
vruler->SetNumberScale(&scale);
}
break;
@ -2183,16 +2178,12 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
t0, pps);
}
// Legacy special-case treatment of log scale
const SpectrogramSettings::ScaleType scaleType = settings.scaleType;
const int minFreq =
scaleType == SpectrogramSettings::stLinear
? settings.GetMinFreq(rate) : settings.GetLogMinFreq(rate);
const int maxFreq =
scaleType == SpectrogramSettings::stLinear
? settings.GetMaxFreq(rate) : settings.GetLogMaxFreq(rate);
float minFreq, maxFreq;
track->GetSpectrumBounds(&minFreq, &maxFreq);
const NumberScale numberScale(settings.GetScale(rate, true));
const SpectrogramSettings::ScaleType scaleType = settings.scaleType;
const NumberScale numberScale(settings.GetScale(minFreq, maxFreq, rate, true));
#ifdef EXPERIMENTAL_FFT_Y_GRID
const float

View File

@ -1037,7 +1037,7 @@ AudacityProject * TrackPanel::GetProject() const
}
/// AS: This gets called on our wx timer events.
void TrackPanel::OnTimer(wxTimerEvent& event)
void TrackPanel::OnTimer(wxTimerEvent& )
{
mTimeCount++;
// AS: If the user is dragging the mouse and there is a track that
@ -3467,7 +3467,9 @@ double TrackPanel::PositionToFrequency(const WaveTrack *wt,
return -1;
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
const NumberScale numberScale(settings.GetScale(rate, false));
float minFreq, maxFreq;
wt->GetSpectrumBounds(&minFreq, &maxFreq);
const NumberScale numberScale(settings.GetScale(minFreq, maxFreq, rate, false));
const double p = double(mouseYCoordinate - trackTopEdge) / trackHeight;
return numberScale.PositionToValue(1.0 - p);
}
@ -3481,7 +3483,9 @@ wxInt64 TrackPanel::FrequencyToPosition(const WaveTrack *wt,
const double rate = wt->GetRate();
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
const NumberScale numberScale(settings.GetScale(rate, false));
float minFreq, maxFreq;
wt->GetSpectrumBounds(&minFreq, &maxFreq);
const NumberScale numberScale(settings.GetScale(minFreq, maxFreq, rate, false));
const float p = numberScale.ValueToPosition(frequency);
return trackTopEdge + wxInt64((1.0 - p) * trackHeight);
}
@ -4612,20 +4616,14 @@ void TrackPanel::HandleWaveTrackVZoom
const double rate = track->GetRate();
const float halfrate = rate / 2;
const SpectrogramSettings &settings = track->GetSpectrogramSettings();
NumberScale scale(track->GetSpectrogramSettings().GetScale(rate, false));
NumberScale scale;
const bool spectral = (track->GetDisplay() == WaveTrack::Spectrum);
const bool spectrumLinear = spectral &&
(track->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLinear);
if (spectral) {
if (spectrumLinear) {
min = settings.GetMinFreq(rate);
max = settings.GetMaxFreq(rate);
}
else {
min = settings.GetLogMinFreq(rate);
max = settings.GetLogMaxFreq(rate);
}
track->GetSpectrumBounds(&min, &max);
scale = (settings.GetScale(min, max, rate, false));
const int fftLength = settings.GetFFTLength();
const float binSize = rate / fftLength;
const int minBins =
@ -4797,28 +4795,9 @@ void TrackPanel::HandleWaveTrackVZoom
}
if (spectral) {
if (spectrumLinear) {
SpectrogramSettings &settings = track->GetSpectrogramSettings();
settings.SetMinFreq(min);
settings.SetMaxFreq(max);
if (partner) {
// To do: share memory with reference counting?
SpectrogramSettings &settings = partner->GetSpectrogramSettings();
settings.SetMinFreq(min);
settings.SetMaxFreq(max);
}
}
else {
SpectrogramSettings &settings = track->GetSpectrogramSettings();
settings.SetLogMinFreq(min);
settings.SetLogMaxFreq(max);
if (partner) {
// To do: share memory with reference counting?
SpectrogramSettings &settings = partner->GetSpectrogramSettings();
settings.SetLogMinFreq(min);
settings.SetLogMaxFreq(max);
}
}
track->SetSpectrumBounds(min, max);
if (partner)
partner->SetSpectrumBounds(min, max);
}
else {
track->SetDisplayBounds(min, max);
@ -6287,36 +6266,23 @@ void TrackPanel::HandleWheelRotationInVRuler
const float delta = steps * movement / height;
SpectrogramSettings &settings = wt->GetIndependentSpectrogramSettings();
const bool isLinear = settings.scaleType == SpectrogramSettings::stLinear;
float bottom, top;
wt->GetSpectrumBounds(&bottom, &top);
const double rate = wt->GetRate();
const float maxFreq = float(rate) / 2.0f;
const NumberScale numberScale(settings.GetScale(rate, false));
const float bound = rate / 2;
const NumberScale numberScale(settings.GetScale(bottom, top, rate, false));
float newTop =
std::min(maxFreq, numberScale.PositionToValue(1.0f + delta));
std::min(bound, numberScale.PositionToValue(1.0f + delta));
const float newBottom =
std::max((isLinear ? 0.0f : 1.0f),
numberScale.PositionToValue(numberScale.ValueToPosition(newTop) - 1.0f));
newTop =
std::min(maxFreq,
std::min(bound,
numberScale.PositionToValue(numberScale.ValueToPosition(newBottom) + 1.0f));
if (isLinear) {
settings.SetMinFreq(newBottom);
settings.SetMaxFreq(newTop);
}
else {
settings.SetLogMinFreq(newBottom);
settings.SetLogMaxFreq(newTop);
}
if (partner) {
SpectrogramSettings &partnerSettings = partner->GetIndependentSpectrogramSettings();
if (isLinear) {
partnerSettings.SetMinFreq(newBottom);
partnerSettings.SetMaxFreq(newTop);
}
else {
partnerSettings.SetLogMinFreq(newBottom);
partnerSettings.SetLogMaxFreq(newTop);
}
}
wt->SetSpectrumBounds(newBottom, newTop);
if (partner)
partner->SetSpectrumBounds(newBottom, newTop);
}
else {
float topLimit = 2.0;

View File

@ -107,6 +107,7 @@ WaveTrack::WaveTrack(DirManager *projDirManager, sampleFormat format, double rat
SetName(GetDefaultName());
mDisplayMin = -1.0;
mDisplayMax = 1.0;
mSpectrumMin = mSpectrumMax = -1; // so values will default to settings
mDisplayNumLocations = 0;
mDisplayLocations = NULL;
mDisplayNumLocationsAllocated = 0;
@ -147,6 +148,8 @@ void WaveTrack::Init(const WaveTrack &orig)
mDisplay = orig.mDisplay;
mDisplayMin = orig.mDisplayMin;
mDisplayMax = orig.mDisplayMax;
mSpectrumMin = orig.mSpectrumMin;
mSpectrumMax = orig.mSpectrumMax;
mDisplayNumLocations = 0;
mDisplayLocations = NULL;
mDisplayNumLocationsAllocated = 0;
@ -297,7 +300,7 @@ void WaveTrack::SetLastdBRange()
mLastdBRange = GetWaveformSettings().dBRange;
}
void WaveTrack::GetDisplayBounds(float *min, float *max)
void WaveTrack::GetDisplayBounds(float *min, float *max) const
{
*min = mDisplayMin;
*max = mDisplayMax;
@ -309,6 +312,56 @@ void WaveTrack::SetDisplayBounds(float min, float max)
mDisplayMax = max;
}
void WaveTrack::GetSpectrumBounds(float *min, float *max) const
{
const double rate = GetRate();
const SpectrogramSettings &settings = GetSpectrogramSettings();
const SpectrogramSettings::ScaleType type = settings.scaleType;
const float top = (rate / 2.);
float bottom;
if (type == SpectrogramSettings::stLinear)
bottom = 0.0f;
else if (type == SpectrogramSettings::stPeriod) {
// special case
const int half = settings.GetFFTLength() / 2;
// EAC returns no data for below this frequency:
const float bin2 = rate / half;
bottom = bin2;
}
else
// logarithmic, etc.
bottom = 1.0f;
{
float spectrumMax = mSpectrumMax;
if (spectrumMax < 0)
spectrumMax = settings.maxFreq;
if (spectrumMax < 0)
*max = top;
else
*max = std::max(bottom, std::min(top, spectrumMax));
}
{
float spectrumMin = mSpectrumMin;
if (spectrumMin < 0)
spectrumMin = settings.minFreq;
if (spectrumMin < 0)
*min = std::max(bottom, top / 1000.0f);
else
*min = std::max(bottom, std::min(top, spectrumMin));
}
}
void WaveTrack::SetSpectrumBounds(float min, float max)
{
mSpectrumMin = min;
mSpectrumMax = max;
}
Track *WaveTrack::Duplicate()
{
return new WaveTrack(*this);

View File

@ -441,8 +441,10 @@ class AUDACITY_DLL_API WaveTrack : public Track {
WaveTrackDisplay GetDisplay() const { return mDisplay; }
void SetDisplay(WaveTrackDisplay display) { mDisplay = display; }
void GetDisplayBounds(float *min, float *max);
void GetDisplayBounds(float *min, float *max) const;
void SetDisplayBounds(float min, float max);
void GetSpectrumBounds(float *min, float *max) const;
void SetSpectrumBounds(float min, float max);
protected:
@ -464,6 +466,9 @@ class AUDACITY_DLL_API WaveTrack : public Track {
//
float mDisplayMin;
float mDisplayMax;
float mSpectrumMin;
float mSpectrumMax;
WaveTrackDisplay mDisplay;
int mLastScaleType; // last scale type choice
int mLastdBRange;

View File

@ -68,8 +68,6 @@ SpectrogramSettings::SpectrogramSettings()
SpectrogramSettings::SpectrogramSettings(const SpectrogramSettings &other)
: minFreq(other.minFreq)
, maxFreq(other.maxFreq)
, logMinFreq(other.logMinFreq)
, logMaxFreq(other.logMaxFreq)
, range(other.range)
, gain(other.gain)
, frequencyGain(other.frequencyGain)
@ -107,8 +105,6 @@ SpectrogramSettings &SpectrogramSettings::operator= (const SpectrogramSettings &
if (this != &other) {
minFreq = other.minFreq;
maxFreq = other.maxFreq;
logMinFreq = other.logMinFreq;
logMaxFreq = other.logMaxFreq;
range = other.range;
gain = other.gain;
frequencyGain = other.frequencyGain;
@ -298,20 +294,6 @@ void SpectrogramSettings::LoadPrefs()
// 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();
}
@ -501,59 +483,6 @@ void SpectrogramSettings::ConvertToActualWindowSizes()
#endif
}
int SpectrogramSettings::GetMinFreq(double rate) const
{
const int top = lrint(rate / 2.);
return std::max(0, std::min(top, minFreq));
}
int SpectrogramSettings::GetMaxFreq(double rate) const
{
const int top = lrint(rate / 2.);
if (maxFreq < 0)
return top;
else
return std::max(0, std::min(top, maxFreq));
}
int SpectrogramSettings::GetLogMinFreq(double rate) const
{
const int top = lrint(rate / 2.);
if (logMinFreq < 0)
return top / 1000.0;
else
return std::max(1, std::min(top, logMinFreq));
}
int SpectrogramSettings::GetLogMaxFreq(double rate) const
{
const int top = lrint(rate / 2.);
if (logMaxFreq < 0)
return top;
else
return std::max(1, std::min(top, logMaxFreq));
}
void SpectrogramSettings::SetMinFreq(int freq)
{
minFreq = freq;
}
void SpectrogramSettings::SetMaxFreq(int freq)
{
maxFreq = freq;
}
void SpectrogramSettings::SetLogMinFreq(int freq)
{
logMinFreq = freq;
}
void SpectrogramSettings::SetLogMaxFreq(int freq)
{
logMaxFreq = freq;
}
int SpectrogramSettings::GetFFTLength() const
{
return windowSize
@ -564,9 +493,8 @@ int SpectrogramSettings::GetFFTLength() const
}
NumberScale SpectrogramSettings::GetScale
(double rate, bool bins) const
(float minFreq, float maxFreq, double rate, bool bins) const
{
int minFreq, maxFreq;
NumberScaleType type = nstLinear;
const int half = GetFFTLength() / 2;
@ -585,31 +513,8 @@ NumberScale SpectrogramSettings::GetScale
type = nstBark; break;
case stErb:
type = nstErb; break;
case stUndertone:
type = nstUndertone; break;
}
switch (scaleType) {
default:
wxASSERT(false);
case stLinear:
minFreq = GetMinFreq(rate);
maxFreq = GetMaxFreq(rate);
break;
case stLogarithmic:
case stMel:
case stBark:
case stErb:
minFreq = GetLogMinFreq(rate);
maxFreq = GetLogMaxFreq(rate);
break;
case stUndertone:
{
const float bin2 = rate / half;
minFreq = std::max(int(0.5 + bin2), GetLogMinFreq(rate));
maxFreq = GetLogMaxFreq(rate);
}
break;
case stPeriod:
type = nstPeriod; break;
}
return NumberScale(type, minFreq, maxFreq,

View File

@ -57,7 +57,7 @@ public:
stMel,
stBark,
stErb,
stUndertone,
stPeriod,
stNumScaleTypes,
};
@ -87,24 +87,12 @@ public:
// If "bins" is false, units are Hz
NumberScale GetScale
(double rate, bool bins) const;
(float minFreq, float maxFreq, double rate, bool bins) const;
private:
int minFreq;
int maxFreq;
int logMinFreq;
int logMaxFreq;
public:
int GetMinFreq(double rate) const;
int GetMaxFreq(double rate) const;
int GetLogMinFreq(double rate) const;
int GetLogMaxFreq(double rate) const;
bool SpectralSelectionEnabled() const;
void SetMinFreq(int freq);
void SetMaxFreq(int freq);
void SetLogMinFreq(int freq);
void SetLogMaxFreq(int freq);
bool SpectralSelectionEnabled() const;
public:
int range;

View File

@ -376,8 +376,12 @@ bool SpectrumPrefs::Apply()
if (mWt) {
if (mDefaulted) {
mWt->SetSpectrogramSettings(NULL);
if (partner)
// ... and so that the vertical scale also defaults:
mWt->SetSpectrumBounds(-1, -1);
if (partner) {
partner->SetSpectrogramSettings(NULL);
partner->SetSpectrumBounds(-1, -1);
}
}
else {
SpectrogramSettings *pSettings =