mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-25 16:48:44 +02:00
Other spectrogram scales, easily defined!! -- and include bug fixes 1038, 1039
This commit is contained in:
commit
981acf0bd2
@ -171,6 +171,7 @@ audacity_SOURCES = \
|
|||||||
MixerBoard.h \
|
MixerBoard.h \
|
||||||
ModuleManager.cpp \
|
ModuleManager.cpp \
|
||||||
ModuleManager.h \
|
ModuleManager.h \
|
||||||
|
NumberScale.h \
|
||||||
PitchName.cpp \
|
PitchName.cpp \
|
||||||
PitchName.h \
|
PitchName.h \
|
||||||
PlatformCompatibility.cpp \
|
PlatformCompatibility.cpp \
|
||||||
|
279
src/NumberScale.h
Normal file
279
src/NumberScale.h
Normal file
@ -0,0 +1,279 @@
|
|||||||
|
/**********************************************************************
|
||||||
|
|
||||||
|
Audacity: A Digital Audio Editor
|
||||||
|
|
||||||
|
NumberScale.h
|
||||||
|
|
||||||
|
Paul Licameli
|
||||||
|
|
||||||
|
**********************************************************************/
|
||||||
|
|
||||||
|
#ifndef __AUDACITY_NUMBER_SCALE__
|
||||||
|
#define __AUDACITY_NUMBER_SCALE__
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cmath>
|
||||||
|
#include <wx/defs.h>
|
||||||
|
#include <wx/debug.h>
|
||||||
|
|
||||||
|
enum NumberScaleType {
|
||||||
|
nstLinear,
|
||||||
|
nstLogarithmic,
|
||||||
|
nstMel,
|
||||||
|
nstBark,
|
||||||
|
nstErb,
|
||||||
|
nstUndertone,
|
||||||
|
|
||||||
|
nstNumScaleTypes,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class NumberScale
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NumberScale(NumberScaleType type,
|
||||||
|
float value0, float value1, float unit)
|
||||||
|
: mType(type)
|
||||||
|
{
|
||||||
|
switch (mType) {
|
||||||
|
case nstLinear:
|
||||||
|
{
|
||||||
|
mValue0 = value0 / unit;
|
||||||
|
mValue1 = value1 / unit;
|
||||||
|
mUnit = 1.0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case nstLogarithmic:
|
||||||
|
{
|
||||||
|
mValue0 = logf(value0 / unit);
|
||||||
|
mValue1 = logf(value1 / unit);
|
||||||
|
mUnit = 1.0;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case nstMel:
|
||||||
|
{
|
||||||
|
mValue0 = hzToMel(value0);
|
||||||
|
mValue1 = hzToMel(value1);
|
||||||
|
mUnit = unit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case nstBark:
|
||||||
|
{
|
||||||
|
mValue0 = hzToBark(value0);
|
||||||
|
mValue1 = hzToBark(value1);
|
||||||
|
mUnit = unit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case nstErb:
|
||||||
|
{
|
||||||
|
mValue0 = hzToErb(value0);
|
||||||
|
mValue1 = hzToErb(value1);
|
||||||
|
mUnit = unit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case nstUndertone:
|
||||||
|
{
|
||||||
|
mValue0 = hzToUndertone(value0);
|
||||||
|
mValue1 = hzToUndertone(value1);
|
||||||
|
mUnit = unit;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wxASSERT(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NumberScale Reversal() const
|
||||||
|
{
|
||||||
|
NumberScale result(*this);
|
||||||
|
std::swap(result.mValue0, result.mValue1);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const NumberScale& other) const
|
||||||
|
{
|
||||||
|
return mType == other.mType
|
||||||
|
&& mValue0 == other.mValue0
|
||||||
|
&& mValue1 == other.mValue1
|
||||||
|
&& mUnit == other.mUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator != (const NumberScale &other) const
|
||||||
|
{
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float hzToMel(float hz)
|
||||||
|
{
|
||||||
|
return 1127 * log(1 + hz / 700);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float melToHz(float mel)
|
||||||
|
{
|
||||||
|
return 700 * (exp(mel / 1127) - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float hzToBark(float hz)
|
||||||
|
{
|
||||||
|
// Traunmueller's formula
|
||||||
|
const float z1 = 26.81 * hz / (1960 + hz) - 0.53;
|
||||||
|
if (z1 < 2.0)
|
||||||
|
return z1 + 0.15 * (2.0 - z1);
|
||||||
|
else if (z1 > 20.1)
|
||||||
|
return z1 + 0.22 * (z1 - 20.1);
|
||||||
|
else
|
||||||
|
return z1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float barkToHz(float z1)
|
||||||
|
{
|
||||||
|
if (z1 < 2.0)
|
||||||
|
z1 = 2.0 + (z1 - 2.0) / 0.85;
|
||||||
|
else if (z1 > 20.1)
|
||||||
|
z1 = 20.1 + (z1 - 20.1) / 1.22;
|
||||||
|
return 1960 * (z1 + 0.53) / (26.28 - z1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float hzToErb(float hz)
|
||||||
|
{
|
||||||
|
return 11.17268 * log(1 + (46.06538 * hz) / (hz + 14678.49));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float erbToHz(float erb)
|
||||||
|
{
|
||||||
|
return 676170.4 / (47.06538 - exp(0.08950404 * erb)) - 14678.49;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float hzToUndertone(float hz)
|
||||||
|
{
|
||||||
|
return -1.0 / std::max (1.0f, hz);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline float undertoneToHz(float u)
|
||||||
|
{
|
||||||
|
return -1.0 / u;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Random access
|
||||||
|
float PositionToValue(float pp) const
|
||||||
|
{
|
||||||
|
switch (mType) {
|
||||||
|
default:
|
||||||
|
wxASSERT(false);
|
||||||
|
case nstLinear:
|
||||||
|
return mValue0 + pp * (mValue1 - mValue0);
|
||||||
|
case nstLogarithmic:
|
||||||
|
return exp(mValue0 + pp * (mValue1 - mValue0));
|
||||||
|
case nstMel:
|
||||||
|
return melToHz(mValue0 + pp * (mValue1 - mValue0)) / mUnit;
|
||||||
|
case nstBark:
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// STL-idiom iteration
|
||||||
|
|
||||||
|
class Iterator
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Iterator(NumberScaleType type, float step, float value, float unit)
|
||||||
|
: mType(type), mStep(step), mValue(value), mUnit(unit)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float operator * () const
|
||||||
|
{
|
||||||
|
switch (mType) {
|
||||||
|
default:
|
||||||
|
wxASSERT(false);
|
||||||
|
case nstLinear:
|
||||||
|
case nstLogarithmic:
|
||||||
|
return mValue;
|
||||||
|
case nstMel:
|
||||||
|
return melToHz(mValue) / mUnit;
|
||||||
|
case nstBark:
|
||||||
|
return barkToHz(mValue) / mUnit;
|
||||||
|
case nstErb:
|
||||||
|
return erbToHz(mValue) / mUnit;
|
||||||
|
case nstUndertone:
|
||||||
|
return undertoneToHz(mValue) / mUnit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator &operator ++()
|
||||||
|
{
|
||||||
|
switch (mType) {
|
||||||
|
case nstLinear:
|
||||||
|
case nstMel:
|
||||||
|
case nstBark:
|
||||||
|
case nstErb:
|
||||||
|
case nstUndertone:
|
||||||
|
mValue += mStep;
|
||||||
|
break;
|
||||||
|
case nstLogarithmic:
|
||||||
|
mValue *= mStep;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
wxASSERT(false);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const NumberScaleType mType;
|
||||||
|
const float mStep;
|
||||||
|
float mValue;
|
||||||
|
float mUnit;
|
||||||
|
};
|
||||||
|
|
||||||
|
Iterator begin(float nPositions) const
|
||||||
|
{
|
||||||
|
switch (mType) {
|
||||||
|
default:
|
||||||
|
wxASSERT(false);
|
||||||
|
case nstLinear:
|
||||||
|
case nstMel:
|
||||||
|
case nstBark:
|
||||||
|
case nstErb:
|
||||||
|
case nstUndertone:
|
||||||
|
return Iterator
|
||||||
|
(mType, (mValue1 - mValue0) / nPositions, mValue0, mUnit);
|
||||||
|
case nstLogarithmic:
|
||||||
|
return Iterator
|
||||||
|
(mType, exp((mValue1 - mValue0) / nPositions), exp(mValue0), mUnit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inverse
|
||||||
|
float ValueToPosition(float val) const
|
||||||
|
{
|
||||||
|
switch (mType) {
|
||||||
|
default:
|
||||||
|
wxASSERT(false);
|
||||||
|
case nstLinear:
|
||||||
|
return ((val - mValue0) / (mValue1 - mValue0));
|
||||||
|
case nstLogarithmic:
|
||||||
|
return ((log(val) - mValue0) / (mValue1 - mValue0));
|
||||||
|
case nstMel:
|
||||||
|
return ((hzToMel(val * mUnit) - mValue0) / (mValue1 - mValue0));
|
||||||
|
case nstBark:
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const NumberScaleType mType;
|
||||||
|
float mValue0;
|
||||||
|
float mValue1;
|
||||||
|
float mUnit;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@ -168,6 +168,7 @@ audio tracks.
|
|||||||
#include "AColor.h"
|
#include "AColor.h"
|
||||||
#include "BlockFile.h"
|
#include "BlockFile.h"
|
||||||
#include "Envelope.h"
|
#include "Envelope.h"
|
||||||
|
#include "NumberScale.h"
|
||||||
#include "Track.h"
|
#include "Track.h"
|
||||||
#include "WaveTrack.h"
|
#include "WaveTrack.h"
|
||||||
#include "LabelTrack.h"
|
#include "LabelTrack.h"
|
||||||
@ -829,6 +830,10 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SpectrogramSettings::stLogarithmic:
|
case SpectrogramSettings::stLogarithmic:
|
||||||
|
case SpectrogramSettings::stMel:
|
||||||
|
case SpectrogramSettings::stBark:
|
||||||
|
case SpectrogramSettings::stErb:
|
||||||
|
case SpectrogramSettings::stUndertone:
|
||||||
{
|
{
|
||||||
// SpectrumLog
|
// SpectrumLog
|
||||||
|
|
||||||
@ -852,6 +857,9 @@ void TrackArtist::UpdateVRuler(Track *t, wxRect & rect)
|
|||||||
vruler->SetRange(maxFreq, minFreq);
|
vruler->SetRange(maxFreq, minFreq);
|
||||||
vruler->SetUnits(wxT(""));
|
vruler->SetUnits(wxT(""));
|
||||||
vruler->SetLog(true);
|
vruler->SetLog(true);
|
||||||
|
NumberScale scale
|
||||||
|
(wt->GetSpectrogramSettings().GetScale(wt->GetRate(), false, false).Reversal());
|
||||||
|
vruler->SetNumberScale(&scale);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2036,7 +2044,6 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
|
|||||||
|
|
||||||
const int display = track->GetDisplay();
|
const int display = track->GetDisplay();
|
||||||
const bool autocorrelation = (WaveTrack::PitchDisplay == display);
|
const bool autocorrelation = (WaveTrack::PitchDisplay == display);
|
||||||
const bool logF = settings.scaleType == SpectrogramSettings::stLogarithmic;
|
|
||||||
|
|
||||||
enum { DASH_LENGTH = 10 /* pixels */ };
|
enum { DASH_LENGTH = 10 /* pixels */ };
|
||||||
|
|
||||||
@ -2115,15 +2122,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
|
|||||||
scaleType == SpectrogramSettings::stLinear
|
scaleType == SpectrogramSettings::stLinear
|
||||||
? settings.GetMaxFreq(rate) : settings.GetLogMaxFreq(rate);
|
? settings.GetMaxFreq(rate) : settings.GetLogMaxFreq(rate);
|
||||||
|
|
||||||
float minBin = ((double)minFreq / binUnit);
|
const NumberScale numberScale(settings.GetScale(rate, true, autocorrelation));
|
||||||
float maxBin = ((double)maxFreq / binUnit);
|
|
||||||
float binPerPx = float(maxBin - minBin) / float(mid.height);
|
|
||||||
|
|
||||||
const float
|
|
||||||
// e=exp(1.0f),
|
|
||||||
lmin = logf(float(minFreq)),
|
|
||||||
lmax = logf(float(maxFreq)),
|
|
||||||
scale = lmax - lmin;
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
||||||
const float
|
const float
|
||||||
@ -2204,135 +2203,124 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
|
|||||||
int *indexes = new int[maxTableSize];
|
int *indexes = new int[maxTableSize];
|
||||||
#endif //EXPERIMENTAL_FIND_NOTES
|
#endif //EXPERIMENTAL_FIND_NOTES
|
||||||
|
|
||||||
for (int xx = 0; xx < hiddenMid.width; ++xx)
|
for (int xx = 0; xx < hiddenMid.width; ++xx) {
|
||||||
{
|
NumberScale::Iterator it = numberScale.begin(mid.height);
|
||||||
if (!logF) {
|
float nextBin = std::max(0.0f, std::min(float(half - 1), *it));
|
||||||
for (int yy = 0; yy < hiddenMid.height; ++yy) {
|
for (int yy = 0; yy < hiddenMid.height; ++yy) {
|
||||||
float bin0 = float(yy) * binPerPx + minBin;
|
const float bin = nextBin;
|
||||||
float bin1 = float(yy + 1) * binPerPx + minBin;
|
nextBin = std::max(0.0f, std::min(float(half - 1), *++it));
|
||||||
|
|
||||||
|
if (settings.scaleType != SpectrogramSettings::stLogarithmic) {
|
||||||
const float value = findValue
|
const float value = findValue
|
||||||
(freq + half * xx, bin0, bin1, half, autocorrelation, gain, range);
|
(freq + half * xx, bin, nextBin, half, autocorrelation, gain, range);
|
||||||
clip->mSpecPxCache->values[xx * hiddenMid.height + yy] = value;
|
clip->mSpecPxCache->values[xx * hiddenMid.height + yy] = value;
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else {
|
// Do we need this legacy experiment still?
|
||||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||||
int maximas=0;
|
int maximas = 0;
|
||||||
const int x0 = half * xx;
|
const int x0 = half * x;
|
||||||
if (fftFindNotes) {
|
if (fftFindNotes) {
|
||||||
for (int i = maxTableSize-1; i >= 0; i--)
|
for (int i = maxTableSize - 1; i >= 0; i--)
|
||||||
indexes[i]=-1;
|
indexes[i] = -1;
|
||||||
|
|
||||||
// Build a table of (most) values, put the index in it.
|
// Build a table of (most) values, put the index in it.
|
||||||
for (int i = int(i0); i < int(i1); i++) {
|
for (int i = int(i0); i < int(i1); i++) {
|
||||||
float freqi=freq[x0 + int(i)];
|
float freqi = freq[x0 + int(i)];
|
||||||
int value=int((freqi+gain+range)/range*(maxTableSize-1));
|
int value = int((freqi + gain + range) / range*(maxTableSize - 1));
|
||||||
if (value < 0)
|
if (value < 0)
|
||||||
value=0;
|
value = 0;
|
||||||
if (value >= maxTableSize)
|
if (value >= maxTableSize)
|
||||||
value=maxTableSize-1;
|
value = maxTableSize - 1;
|
||||||
indexes[value]=i;
|
indexes[value] = i;
|
||||||
}
|
}
|
||||||
// Build from the indices an array of maxima.
|
// Build from the indices an array of maxima.
|
||||||
for (int i = maxTableSize - 1; i >= 0; i--) {
|
for (int i = maxTableSize - 1; i >= 0; i--) {
|
||||||
int index = indexes[i];
|
int index = indexes[i];
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
float freqi = freq[x0 + index];
|
float freqi = freq[x0 + index];
|
||||||
if (freqi < findNotesMinA)
|
if (freqi < findNotesMinA)
|
||||||
break;
|
|
||||||
|
|
||||||
bool ok = true;
|
|
||||||
for (int m = 0; m < maximas; m++) {
|
|
||||||
// Avoid to store very close maxima.
|
|
||||||
float maxm = maxima[m];
|
|
||||||
if (maxm / index < minDistance && index / maxm < minDistance) {
|
|
||||||
ok = false;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
for (int m = 0; m < maximas; m++) {
|
||||||
|
// Avoid to store very close maxima.
|
||||||
|
float maxm = maxima[m];
|
||||||
|
if (maxm / index < minDistance && index / maxm < minDistance) {
|
||||||
|
ok = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ok) {
|
||||||
|
maxima[maximas++] = index;
|
||||||
|
if (maximas >= numberOfMaxima)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ok) {
|
|
||||||
maxima[maximas++] = index;
|
|
||||||
if (maximas >= numberOfMaxima)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// The f2pix helper macro converts a frequency into a pixel coordinate.
|
// The f2pix helper macro converts a frequency into a pixel coordinate.
|
||||||
#define f2pix(f) (logf(f)-lmins)/(lmaxs-lmins)*hiddenMid.height
|
#define f2pix(f) (logf(f)-lmins)/(lmaxs-lmins)*hiddenMid.height
|
||||||
|
|
||||||
// Possibly quantize the maxima frequencies and create the pixel block limits.
|
// Possibly quantize the maxima frequencies and create the pixel block limits.
|
||||||
for (int i=0; i < maximas; i++) {
|
for (int i = 0; i < maximas; i++) {
|
||||||
int index=maxima[i];
|
int index = maxima[i];
|
||||||
float f = float(index)*bin2f;
|
float f = float(index)*bin2f;
|
||||||
if (findNotesQuantize)
|
if (findNotesQuantize)
|
||||||
{ f = expf(int(log(f/440)/log2*12-0.5)/12.0f*log2)*440;
|
{
|
||||||
maxima[i] = f*f2bin;
|
f = expf(int(log(f / 440) / log2 * 12 - 0.5) / 12.0f*log2) * 440;
|
||||||
|
maxima[i] = f*f2bin;
|
||||||
|
}
|
||||||
|
float f0 = expf((log(f / 440) / log2 * 24 - 1) / 24.0f*log2) * 440;
|
||||||
|
maxima0[i] = f2pix(f0);
|
||||||
|
float f1 = expf((log(f / 440) / log2 * 24 + 1) / 24.0f*log2) * 440;
|
||||||
|
maxima1[i] = f2pix(f1);
|
||||||
}
|
}
|
||||||
float f0 = expf((log(f/440)/log2*24-1)/24.0f*log2)*440;
|
|
||||||
maxima0[i] = f2pix(f0);
|
|
||||||
float f1 = expf((log(f/440)/log2*24+1)/24.0f*log2)*440;
|
|
||||||
maxima1[i] = f2pix(f1);
|
|
||||||
}
|
}
|
||||||
}
|
int it = 0;
|
||||||
int it=0;
|
int oldBin0 = -1;
|
||||||
int oldBin0=-1;
|
bool inMaximum = false;
|
||||||
bool inMaximum = false;
|
|
||||||
#endif //EXPERIMENTAL_FIND_NOTES
|
#endif //EXPERIMENTAL_FIND_NOTES
|
||||||
|
|
||||||
double yy2_base = exp(lmin) / binUnit;
|
|
||||||
float yy2 = yy2_base;
|
|
||||||
double exp_scale_per_height = exp(scale / hiddenMid.height);
|
|
||||||
for (int yy = 0; yy < hiddenMid.height; ++yy) {
|
|
||||||
if (int(yy2) >= half)
|
|
||||||
yy2=half-1;
|
|
||||||
if (yy2<0)
|
|
||||||
yy2=0;
|
|
||||||
float bin0 = float(yy2);
|
|
||||||
yy2_base *= exp_scale_per_height;
|
|
||||||
float yy3 = yy2_base;
|
|
||||||
if (int(yy3)>=half)
|
|
||||||
yy3=half-1;
|
|
||||||
if (yy3<0)
|
|
||||||
yy3=0;
|
|
||||||
float bin1 = float(yy3);
|
|
||||||
float value;
|
float value;
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_FIND_NOTES
|
#ifdef EXPERIMENTAL_FIND_NOTES
|
||||||
if (fftFindNotes) {
|
if (fftFindNotes) {
|
||||||
if (it < maximas) {
|
if (it < maximas) {
|
||||||
float i0=maxima0[it];
|
float i0 = maxima0[it];
|
||||||
if (yy >= i0)
|
if (yy >= i0)
|
||||||
inMaximum = true;
|
inMaximum = true;
|
||||||
|
|
||||||
if (inMaximum) {
|
if (inMaximum) {
|
||||||
float i1=maxima1[it];
|
float i1 = maxima1[it];
|
||||||
if (yy+1 <= i1) {
|
if (yy + 1 <= i1) {
|
||||||
value=findValue(freq + x0, bin0, bin1, half, autocorrelation, gain, range);
|
value = findValue(freq + x0, bin, nextBin, half, autocorrelation, gain, range);
|
||||||
if (value < findNotesMinA)
|
if (value < findNotesMinA)
|
||||||
value = minColor;
|
value = minColor;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
it++;
|
it++;
|
||||||
inMaximum = false;
|
inMaximum = false;
|
||||||
value = minColor;
|
value = minColor;
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
value = minColor;
|
value = minColor;
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
value = minColor;
|
value = minColor;
|
||||||
} else
|
}
|
||||||
|
else
|
||||||
#endif //EXPERIMENTAL_FIND_NOTES
|
#endif //EXPERIMENTAL_FIND_NOTES
|
||||||
{
|
{
|
||||||
value = findValue
|
value = findValue
|
||||||
(freq + half * xx, bin0, bin1, half, autocorrelation, gain, range);
|
(freq + half * xx, bin, nextBin, half, autocorrelation, gain, range);
|
||||||
}
|
}
|
||||||
clip->mSpecPxCache->values[xx * hiddenMid.height + yy] = value;
|
clip->mSpecPxCache->values[xx * hiddenMid.height + yy] = value;
|
||||||
yy2 = yy2_base;
|
} // logF
|
||||||
} // each yy
|
} // each yy
|
||||||
} // is logF
|
|
||||||
} // each xx
|
} // each xx
|
||||||
|
|
||||||
} // updating cache
|
} // updating cache
|
||||||
|
|
||||||
float selBinLo = freqLo / binUnit;
|
float selBinLo = freqLo / binUnit;
|
||||||
@ -2384,85 +2372,42 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
|
|||||||
(zoomInfo.PositionToTime(xx + 1, -leftOffset) - tOffset)
|
(zoomInfo.PositionToTime(xx + 1, -leftOffset) - tOffset)
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: The logF and non-logF case are very similar.
|
NumberScale::Iterator it = numberScale.begin(mid.height);
|
||||||
// They should be merged and simplified.
|
float nextBin = std::max(0.0f, std::min(float(half - 1), *it));
|
||||||
if (!logF)
|
for (int yy = 0; yy < hiddenMid.height; ++yy) {
|
||||||
{
|
const float bin = nextBin;
|
||||||
for (int yy = 0; yy < hiddenMid.height; ++yy) {
|
nextBin = std::max(0.0f, std::min(float(half - 1), *++it));
|
||||||
float bin0 = float(yy) * binPerPx + minBin;
|
|
||||||
float bin1 = float(yy + 1) * binPerPx + minBin;
|
|
||||||
|
|
||||||
// For spectral selection, determine what colour
|
// For spectral selection, determine what colour
|
||||||
// set to use. We use a darker selection if
|
// set to use. We use a darker selection if
|
||||||
// in both spectral range and time range.
|
// in both spectral range and time range.
|
||||||
|
|
||||||
AColor::ColorGradientChoice selected =
|
AColor::ColorGradientChoice selected = AColor::ColorGradientUnselected;
|
||||||
AColor::ColorGradientUnselected;
|
// If we are in the time selected range, then we may use a different color set.
|
||||||
// If we are in the time selected range, then we may use a different color set.
|
if (ssel0 <= w0 && w1 < ssel1)
|
||||||
if (ssel0 <= w0 && w1 < ssel1)
|
selected =
|
||||||
selected = ChooseColorSet(bin0, bin1, selBinLo, selBinCenter, selBinHi,
|
ChooseColorSet(bin, nextBin, selBinLo, selBinCenter, selBinHi,
|
||||||
(xx + leftOffset - hiddenLeftOffset) / DASH_LENGTH, isSpectral);
|
(xx + leftOffset - hiddenLeftOffset) / DASH_LENGTH, isSpectral);
|
||||||
|
const float value = uncached
|
||||||
unsigned char rv, gv, bv;
|
? findValue(uncached, bin, nextBin, half, autocorrelation, gain, range)
|
||||||
const float value = uncached
|
: clip->mSpecPxCache->values[correctedX * hiddenMid.height + yy];
|
||||||
? findValue(uncached, bin0, bin1, half, autocorrelation, gain, range)
|
unsigned char rv, gv, bv;
|
||||||
: clip->mSpecPxCache->values[correctedX * hiddenMid.height + yy];
|
GetColorGradient(value, selected, isGrayscale, &rv, &gv, &bv);
|
||||||
GetColorGradient(value, selected, isGrayscale, &rv, &gv, &bv);
|
|
||||||
int px = ((mid.height - 1 - yy) * mid.width + xx) * 3;
|
|
||||||
data[px++] = rv;
|
|
||||||
data[px++] = gv;
|
|
||||||
data[px] = bv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else //logF
|
|
||||||
{
|
|
||||||
double yy2_base=exp(lmin)/binUnit;
|
|
||||||
float yy2 = yy2_base;
|
|
||||||
double exp_scale_per_height = exp(scale / hiddenMid.height);
|
|
||||||
for (int yy = 0; yy < hiddenMid.height; ++yy) {
|
|
||||||
if (int(yy2)>=half)
|
|
||||||
yy2=half-1;
|
|
||||||
if (yy2<0)
|
|
||||||
yy2=0;
|
|
||||||
float bin0 = float(yy2);
|
|
||||||
yy2_base *= exp_scale_per_height;
|
|
||||||
float yy3 = yy2_base;
|
|
||||||
if (int(yy3)>=half)
|
|
||||||
yy3=half-1;
|
|
||||||
if (yy3<0)
|
|
||||||
yy3=0;
|
|
||||||
float bin1 = float(yy3);
|
|
||||||
|
|
||||||
AColor::ColorGradientChoice selected = AColor::ColorGradientUnselected;
|
|
||||||
// If we are in the time selected range, then we may use a different color set.
|
|
||||||
if (ssel0 <= w0 && w1 < ssel1)
|
|
||||||
selected = ChooseColorSet(
|
|
||||||
bin0, bin1, selBinLo, selBinCenter, selBinHi,
|
|
||||||
(xx + leftOffset - hiddenLeftOffset) / DASH_LENGTH, isSpectral);
|
|
||||||
|
|
||||||
unsigned char rv, gv, bv;
|
|
||||||
const float value = uncached
|
|
||||||
? findValue(uncached, bin0, bin1, half, autocorrelation, gain, range)
|
|
||||||
: clip->mSpecPxCache->values[correctedX * hiddenMid.height + yy];
|
|
||||||
GetColorGradient(value, selected, isGrayscale, &rv, &gv, &bv);
|
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
#ifdef EXPERIMENTAL_FFT_Y_GRID
|
||||||
if (fftYGrid && yGrid[yy]) {
|
if (fftYGrid && yGrid[yy]) {
|
||||||
rv /= 1.1f;
|
rv /= 1.1f;
|
||||||
gv /= 1.1f;
|
gv /= 1.1f;
|
||||||
bv /= 1.1f;
|
bv /= 1.1f;
|
||||||
}
|
}
|
||||||
#endif //EXPERIMENTAL_FFT_Y_GRID
|
#endif //EXPERIMENTAL_FFT_Y_GRID
|
||||||
|
|
||||||
int px = ((mid.height - 1 - yy) * mid.width + xx) * 3;
|
int px = ((mid.height - 1 - yy) * mid.width + xx) * 3;
|
||||||
data[px++] = rv;
|
data[px++] = rv;
|
||||||
data[px++] = gv;
|
data[px++] = gv;
|
||||||
data[px] = bv;
|
data[px] = bv;
|
||||||
|
} // each yy
|
||||||
yy2 = yy2_base;
|
} // each xx
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wxBitmap converted = wxBitmap(*image);
|
wxBitmap converted = wxBitmap(*image);
|
||||||
|
|
||||||
|
@ -201,6 +201,7 @@ is time to refresh some aspect of the screen.
|
|||||||
#include "MixerBoard.h"
|
#include "MixerBoard.h"
|
||||||
|
|
||||||
#include "NoteTrack.h"
|
#include "NoteTrack.h"
|
||||||
|
#include "NumberScale.h"
|
||||||
#include "Prefs.h"
|
#include "Prefs.h"
|
||||||
#include "Project.h"
|
#include "Project.h"
|
||||||
#include "Snap.h"
|
#include "Snap.h"
|
||||||
@ -1802,22 +1803,15 @@ void TrackPanel::SetCursorAndTipWhenInLabelTrack( LabelTrack * pLT,
|
|||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// This returns true if we're a spectral editing track.
|
// This returns true if we're a spectral editing track.
|
||||||
inline bool isSpectralSelectionTrack(const Track *pTrack, bool *pLogf = NULL) {
|
inline bool isSpectralSelectionTrack(const Track *pTrack) {
|
||||||
if (pTrack &&
|
if (pTrack &&
|
||||||
pTrack->GetKind() == Track::Wave) {
|
pTrack->GetKind() == Track::Wave) {
|
||||||
const WaveTrack *const wt = static_cast<const WaveTrack*>(pTrack);
|
const WaveTrack *const wt = static_cast<const WaveTrack*>(pTrack);
|
||||||
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
||||||
const int display = wt->GetDisplay();
|
const int display = wt->GetDisplay();
|
||||||
if (pLogf) {
|
|
||||||
const bool logF =
|
|
||||||
settings.scaleType == SpectrogramSettings::stLogarithmic;
|
|
||||||
*pLogf = logF;
|
|
||||||
}
|
|
||||||
return (display == WaveTrack::Spectrum) && settings.SpectralSelectionEnabled();
|
return (display == WaveTrack::Spectrum) && settings.SpectralSelectionEnabled();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (pLogf)
|
|
||||||
*pLogf = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1941,9 +1935,8 @@ void TrackPanel::SetCursorAndTipWhenSelectTool( Track * t,
|
|||||||
const bool bShiftDown = event.ShiftDown();
|
const bool bShiftDown = event.ShiftDown();
|
||||||
|
|
||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
bool logF;
|
|
||||||
if ( (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER) &&
|
if ( (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER) &&
|
||||||
isSpectralSelectionTrack(t, &logF)) {
|
isSpectralSelectionTrack(t)) {
|
||||||
// Not shift-down, but center frequency snapping toggle is on
|
// Not shift-down, but center frequency snapping toggle is on
|
||||||
*ppTip = _("Click and drag to set frequency bandwidth.");
|
*ppTip = _("Click and drag to set frequency bandwidth.");
|
||||||
*ppCursor = mEnvelopeCursor;
|
*ppCursor = mEnvelopeCursor;
|
||||||
@ -2679,9 +2672,8 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
|
|||||||
// preferences now
|
// preferences now
|
||||||
if (mAdjustSelectionEdges) {
|
if (mAdjustSelectionEdges) {
|
||||||
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
|
||||||
bool logF;
|
|
||||||
if (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER &&
|
if (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER &&
|
||||||
isSpectralSelectionTrack(pTrack, &logF)) {
|
isSpectralSelectionTrack(pTrack)) {
|
||||||
// Ignore whether we are inside the time selection.
|
// Ignore whether we are inside the time selection.
|
||||||
// Exit center-snapping, start dragging the width.
|
// Exit center-snapping, start dragging the width.
|
||||||
mFreqSelMode = FREQ_SEL_PINNED_CENTER;
|
mFreqSelMode = FREQ_SEL_PINNED_CENTER;
|
||||||
@ -3072,10 +3064,9 @@ void TrackPanel::MoveSnappingFreqSelection (int mouseYCoordinate,
|
|||||||
int trackTopEdge,
|
int trackTopEdge,
|
||||||
int trackHeight, Track *pTrack)
|
int trackHeight, Track *pTrack)
|
||||||
{
|
{
|
||||||
bool logF;
|
|
||||||
if (pTrack &&
|
if (pTrack &&
|
||||||
pTrack->GetSelected() &&
|
pTrack->GetSelected() &&
|
||||||
isSpectralSelectionTrack(pTrack, &logF)) {
|
isSpectralSelectionTrack(pTrack)) {
|
||||||
WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
|
WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
|
||||||
// PRL:
|
// PRL:
|
||||||
// What happens if center snapping selection began in one spectrogram track,
|
// What happens if center snapping selection began in one spectrogram track,
|
||||||
@ -3086,7 +3077,7 @@ void TrackPanel::MoveSnappingFreqSelection (int mouseYCoordinate,
|
|||||||
const double rate = wt->GetRate();
|
const double rate = wt->GetRate();
|
||||||
const double frequency =
|
const double frequency =
|
||||||
PositionToFrequency(wt, false, mouseYCoordinate,
|
PositionToFrequency(wt, false, mouseYCoordinate,
|
||||||
trackTopEdge, trackHeight, logF);
|
trackTopEdge, trackHeight);
|
||||||
const double snappedFrequency =
|
const double snappedFrequency =
|
||||||
mFrequencySnapper->FindPeak(frequency, NULL);
|
mFrequencySnapper->FindPeak(frequency, NULL);
|
||||||
const double maxRatio = findMaxRatio(snappedFrequency, rate);
|
const double maxRatio = findMaxRatio(snappedFrequency, rate);
|
||||||
@ -3118,13 +3109,12 @@ void TrackPanel::StartFreqSelection (int mouseYCoordinate, int trackTopEdge,
|
|||||||
mFreqSelMode = FREQ_SEL_INVALID;
|
mFreqSelMode = FREQ_SEL_INVALID;
|
||||||
mFreqSelPin = SelectedRegion::UndefinedFrequency;
|
mFreqSelPin = SelectedRegion::UndefinedFrequency;
|
||||||
|
|
||||||
bool logF;
|
if (isSpectralSelectionTrack(pTrack)) {
|
||||||
if (isSpectralSelectionTrack(pTrack, &logF)) {
|
|
||||||
mFreqSelTrack = static_cast<WaveTrack*>(pTrack);
|
mFreqSelTrack = static_cast<WaveTrack*>(pTrack);
|
||||||
mFreqSelMode = FREQ_SEL_FREE;
|
mFreqSelMode = FREQ_SEL_FREE;
|
||||||
mFreqSelPin =
|
mFreqSelPin =
|
||||||
PositionToFrequency(mFreqSelTrack, false, mouseYCoordinate,
|
PositionToFrequency(mFreqSelTrack, false, mouseYCoordinate,
|
||||||
trackTopEdge, trackHeight, logF);
|
trackTopEdge, trackHeight);
|
||||||
mViewInfo->selectedRegion.setFrequencies(mFreqSelPin, mFreqSelPin);
|
mViewInfo->selectedRegion.setFrequencies(mFreqSelPin, mFreqSelPin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3143,12 +3133,10 @@ void TrackPanel::ExtendFreqSelection(int mouseYCoordinate, int trackTopEdge,
|
|||||||
// started, and that is of a spectrogram display type.
|
// started, and that is of a spectrogram display type.
|
||||||
|
|
||||||
const WaveTrack* wt = mFreqSelTrack;
|
const WaveTrack* wt = mFreqSelTrack;
|
||||||
const bool logF =
|
|
||||||
wt->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLogarithmic;
|
|
||||||
const double rate = wt->GetRate();
|
const double rate = wt->GetRate();
|
||||||
const double frequency =
|
const double frequency =
|
||||||
PositionToFrequency(wt, true, mouseYCoordinate,
|
PositionToFrequency(wt, true, mouseYCoordinate,
|
||||||
trackTopEdge, trackHeight, logF);
|
trackTopEdge, trackHeight);
|
||||||
|
|
||||||
// Dragging center?
|
// Dragging center?
|
||||||
if (mFreqSelMode == FREQ_SEL_DRAG_CENTER) {
|
if (mFreqSelMode == FREQ_SEL_DRAG_CENTER) {
|
||||||
@ -3527,8 +3515,7 @@ double TrackPanel::PositionToFrequency(const WaveTrack *wt,
|
|||||||
bool maySnap,
|
bool maySnap,
|
||||||
wxInt64 mouseYCoordinate,
|
wxInt64 mouseYCoordinate,
|
||||||
wxInt64 trackTopEdge,
|
wxInt64 trackTopEdge,
|
||||||
int trackHeight,
|
int trackHeight) const
|
||||||
bool logF) const
|
|
||||||
{
|
{
|
||||||
const double rate = wt->GetRate();
|
const double rate = wt->GetRate();
|
||||||
|
|
||||||
@ -3540,57 +3527,23 @@ double TrackPanel::PositionToFrequency(const WaveTrack *wt,
|
|||||||
trackTopEdge + trackHeight - mouseYCoordinate < FREQ_SNAP_DISTANCE)
|
trackTopEdge + trackHeight - mouseYCoordinate < FREQ_SNAP_DISTANCE)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
||||||
|
const NumberScale numberScale(settings.GetScale(rate, false, false));
|
||||||
const double p = double(mouseYCoordinate - trackTopEdge) / trackHeight;
|
const double p = double(mouseYCoordinate - trackTopEdge) / trackHeight;
|
||||||
|
return numberScale.PositionToValue(1.0 - p);
|
||||||
if (logF)
|
|
||||||
{
|
|
||||||
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
|
||||||
const double maxFreq = settings.GetLogMaxFreq(rate);
|
|
||||||
const double minFreq = settings.GetLogMinFreq(rate);
|
|
||||||
return exp(p * log(minFreq) + (1.0 - p) * log(maxFreq));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
|
||||||
const double maxFreq = settings.GetMaxFreq(rate);
|
|
||||||
const double minFreq = settings.GetMinFreq(rate);
|
|
||||||
return p * minFreq + (1.0 - p) * maxFreq;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a frequency to screen y position.
|
/// Converts a frequency to screen y position.
|
||||||
wxInt64 TrackPanel::FrequencyToPosition(const WaveTrack *wt,
|
wxInt64 TrackPanel::FrequencyToPosition(const WaveTrack *wt,
|
||||||
double frequency,
|
double frequency,
|
||||||
wxInt64 trackTopEdge,
|
wxInt64 trackTopEdge,
|
||||||
int trackHeight,
|
int trackHeight) const
|
||||||
bool logF) const
|
|
||||||
{
|
{
|
||||||
const double rate = wt->GetRate();
|
const double rate = wt->GetRate();
|
||||||
double p = 0;
|
|
||||||
|
|
||||||
if (logF)
|
|
||||||
{
|
|
||||||
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
|
||||||
const double maxFreq = settings.GetLogMaxFreq(rate);
|
|
||||||
const double minFreq = settings.GetLogMinFreq(rate);
|
|
||||||
if (maxFreq > minFreq)
|
|
||||||
{
|
|
||||||
const double
|
|
||||||
logFrequency = log(frequency < 1.0 ? 1.0 : frequency),
|
|
||||||
logMinFreq = log(minFreq),
|
|
||||||
logMaxFreq = log(maxFreq);
|
|
||||||
p = (logFrequency - logMinFreq) / (logMaxFreq - logMinFreq);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
|
||||||
const double maxFreq = settings.GetMaxFreq(rate);
|
|
||||||
const double minFreq = settings.GetMinFreq(rate);
|
|
||||||
if (maxFreq > minFreq)
|
|
||||||
p = (frequency - minFreq) / (maxFreq - minFreq);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
|
||||||
|
const NumberScale numberScale(settings.GetScale(rate, false, false));
|
||||||
|
const float p = numberScale.ValueToPosition(frequency);
|
||||||
return trackTopEdge + wxInt64((1.0 - p) * trackHeight);
|
return trackTopEdge + wxInt64((1.0 - p) * trackHeight);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -3671,18 +3624,17 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
|
|||||||
bool chooseTime = true;
|
bool chooseTime = true;
|
||||||
bool chooseBottom = true;
|
bool chooseBottom = true;
|
||||||
bool chooseCenter = false;
|
bool chooseCenter = false;
|
||||||
bool logF;
|
|
||||||
// Consider adjustment of frequencies only if mouse is
|
// Consider adjustment of frequencies only if mouse is
|
||||||
// within the time boundaries
|
// within the time boundaries
|
||||||
if (!mViewInfo->selectedRegion.isPoint() &&
|
if (!mViewInfo->selectedRegion.isPoint() &&
|
||||||
t0 <= selend && selend < t1 &&
|
t0 <= selend && selend < t1 &&
|
||||||
isSpectralSelectionTrack(pTrack, &logF)) {
|
isSpectralSelectionTrack(pTrack)) {
|
||||||
const WaveTrack *const wt = static_cast<const WaveTrack*>(pTrack);
|
const WaveTrack *const wt = static_cast<const WaveTrack*>(pTrack);
|
||||||
const wxInt64 bottomSel = (f0 >= 0)
|
const wxInt64 bottomSel = (f0 >= 0)
|
||||||
? FrequencyToPosition(wt, f0, rect.y, rect.height, logF)
|
? FrequencyToPosition(wt, f0, rect.y, rect.height)
|
||||||
: rect.y + rect.height;
|
: rect.y + rect.height;
|
||||||
const wxInt64 topSel = (f1 >= 0)
|
const wxInt64 topSel = (f1 >= 0)
|
||||||
? FrequencyToPosition(wt, f1, rect.y, rect.height, logF)
|
? FrequencyToPosition(wt, f1, rect.y, rect.height)
|
||||||
: rect.y;
|
: rect.y;
|
||||||
wxInt64 signedBottomDist = int(event.m_y - bottomSel);
|
wxInt64 signedBottomDist = int(event.m_y - bottomSel);
|
||||||
wxInt64 verticalDist = abs(signedBottomDist);
|
wxInt64 verticalDist = abs(signedBottomDist);
|
||||||
@ -3700,7 +3652,7 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
|
|||||||
#endif
|
#endif
|
||||||
) {
|
) {
|
||||||
const wxInt64 centerSel =
|
const wxInt64 centerSel =
|
||||||
FrequencyToPosition(wt, fc, rect.y, rect.height, logF);
|
FrequencyToPosition(wt, fc, rect.y, rect.height);
|
||||||
const wxInt64 centerDist = abs(int(event.m_y - centerSel));
|
const wxInt64 centerDist = abs(int(event.m_y - centerSel));
|
||||||
if (centerDist < verticalDist)
|
if (centerDist < verticalDist)
|
||||||
chooseCenter = true, verticalDist = centerDist,
|
chooseCenter = true, verticalDist = centerDist,
|
||||||
@ -4641,7 +4593,6 @@ void TrackPanel::HandleVZoomDrag( wxMouseEvent & event )
|
|||||||
/// - Zoom in; ensure we don't go too large.
|
/// - Zoom in; ensure we don't go too large.
|
||||||
void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event )
|
void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event )
|
||||||
{
|
{
|
||||||
int minBins = 0;
|
|
||||||
if (!mCapturedTrack)
|
if (!mCapturedTrack)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -4671,6 +4622,7 @@ void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event )
|
|||||||
// don't do anything if track is not wave
|
// don't do anything if track is not wave
|
||||||
if (mCapturedTrack->GetKind() != Track::Wave)
|
if (mCapturedTrack->GetKind() != Track::Wave)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
WaveTrack *track = static_cast<WaveTrack*>(mCapturedTrack);
|
WaveTrack *track = static_cast<WaveTrack*>(mCapturedTrack);
|
||||||
WaveTrack *partner = static_cast<WaveTrack *>(mTracks->GetLink(track));
|
WaveTrack *partner = static_cast<WaveTrack *>(mTracks->GetLink(track));
|
||||||
int height = track->GetHeight();
|
int height = track->GetHeight();
|
||||||
@ -4683,223 +4635,173 @@ void TrackPanel::HandleVZoomButtonUp( wxMouseEvent & event )
|
|||||||
mZoomStart = temp;
|
mZoomStart = temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
float min, max, c, l, binSize = 0.0;
|
float min, max, c, l, minBand = 0;
|
||||||
const double rate = track->GetRate();
|
const double rate = track->GetRate();
|
||||||
|
const float halfrate = rate / 2;
|
||||||
|
const SpectrogramSettings &settings = track->GetSpectrogramSettings();
|
||||||
|
NumberScale scale(track->GetSpectrogramSettings().GetScale(rate, false, false));
|
||||||
const bool spectral = (track->GetDisplay() == WaveTrack::Spectrum);
|
const bool spectral = (track->GetDisplay() == WaveTrack::Spectrum);
|
||||||
const bool spectrumLinear = spectral &&
|
const bool spectrumLinear = spectral &&
|
||||||
(track->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLinear);
|
(track->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLinear);
|
||||||
const bool spectrumLog = spectral &&
|
|
||||||
(track->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLogarithmic);
|
if (spectral) {
|
||||||
if (spectrumLinear) {
|
if (spectrumLinear) {
|
||||||
const SpectrogramSettings &settings = track->GetSpectrogramSettings();
|
min = settings.GetMinFreq(rate);
|
||||||
min = settings.GetMinFreq(rate);
|
max = settings.GetMaxFreq(rate);
|
||||||
max = settings.GetMaxFreq(rate);
|
}
|
||||||
|
else {
|
||||||
|
min = settings.GetLogMinFreq(rate);
|
||||||
|
max = settings.GetLogMaxFreq(rate);
|
||||||
|
}
|
||||||
const int fftLength = settings.GetFFTLength(false);
|
const int fftLength = settings.GetFFTLength(false);
|
||||||
binSize = rate / fftLength;
|
const float binSize = rate / fftLength;
|
||||||
minBins = std::min(10, fftLength / 2); //minimum 10 freq bins, unless there are less
|
const int minBins =
|
||||||
}
|
std::min(10, fftLength / 2); //minimum 10 freq bins, unless there are less
|
||||||
else if (spectrumLog) {
|
minBand = minBins * binSize;
|
||||||
const SpectrogramSettings &settings = track->GetSpectrogramSettings();
|
|
||||||
min = settings.GetLogMinFreq(rate);
|
|
||||||
max = settings.GetLogMaxFreq(rate);
|
|
||||||
const int fftLength = settings.GetFFTLength(false);
|
|
||||||
binSize = rate / fftLength;
|
|
||||||
minBins = std::min(10, fftLength / 2); //minimum 10 freq bins, unless there are less
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
track->GetDisplayBounds(&min, &max);
|
track->GetDisplayBounds(&min, &max);
|
||||||
|
|
||||||
if (IsDragZooming()) {
|
if (IsDragZooming()) {
|
||||||
// Drag Zoom
|
// Drag Zoom
|
||||||
float p1, p2, tmin, tmax;
|
const float tmin = min, tmax = max;
|
||||||
tmin=min;
|
|
||||||
tmax=max;
|
|
||||||
|
|
||||||
if(spectrumLog) {
|
if (spectral) {
|
||||||
double xmin = 1-(mZoomEnd - ypos) / (float)height;
|
double xmin = 1 - (mZoomEnd - ypos) / (float)height;
|
||||||
double xmax = 1-(mZoomStart - ypos) / (float)height;
|
double xmax = 1 - (mZoomStart - ypos) / (float)height;
|
||||||
double lmin=log10(tmin), lmax=log10(tmax);
|
const float middle = (xmin + xmax) / 2;
|
||||||
double d=lmax-lmin;
|
const float middleValue = scale.PositionToValue(middle);
|
||||||
min=std::max(1.0, pow(10, xmin*d+lmin));
|
|
||||||
max=std::min(rate/2.0, pow(10, xmax*d+lmin));
|
min = std::max(spectrumLinear ? 0.0f : 1.0f,
|
||||||
// Enforce vertical zoom limits
|
std::min(middleValue - minBand / 2,
|
||||||
// done in the linear freq domain for now, but not too far out
|
scale.PositionToValue(xmin)
|
||||||
if(max < min + minBins * binSize)
|
));
|
||||||
max = min + minBins * binSize;
|
max = std::min(halfrate,
|
||||||
if(max > rate/2.) {
|
std::max(middleValue + minBand / 2,
|
||||||
max = rate/2.;
|
scale.PositionToValue(xmax)
|
||||||
min = max - minBins * binSize;
|
));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
p1 = (mZoomStart - ypos) / (float)height;
|
const float p1 = (mZoomStart - ypos) / (float)height;
|
||||||
p2 = (mZoomEnd - ypos) / (float)height;
|
const float p2 = (mZoomEnd - ypos) / (float)height;
|
||||||
max = (tmax * (1.0-p1) + tmin * p1);
|
max = (tmax * (1.0-p1) + tmin * p1);
|
||||||
min = (tmax * (1.0-p2) + tmin * p2);
|
min = (tmax * (1.0-p2) + tmin * p2);
|
||||||
|
|
||||||
// Enforce vertical zoom limits
|
// Waveform view - allow zooming down to a range of ZOOMLIMIT
|
||||||
if(spectrumLinear) {
|
if (max - min < ZOOMLIMIT) { // if user attempts to go smaller...
|
||||||
if(min < 0.)
|
c = (min+max)/2; // ...set centre of view to centre of dragged area and top/bottom to ZOOMLIMIT/2 above/below
|
||||||
min = 0.;
|
min = c - ZOOMLIMIT/2.0;
|
||||||
if(max < min + minBins * binSize)
|
max = c + ZOOMLIMIT/2.0;
|
||||||
max = min + minBins * binSize;
|
|
||||||
if(max > rate/2.) {
|
|
||||||
max = rate/2.;
|
|
||||||
min = max - minBins * binSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Waveform view - allow zooming down to a range of ZOOMLIMIT
|
|
||||||
if (max - min < ZOOMLIMIT) { // if user attempts to go smaller...
|
|
||||||
c = (min+max)/2; // ...set centre of view to centre of dragged area and top/bottom to ZOOMLIMIT/2 above/below
|
|
||||||
min = c - ZOOMLIMIT/2.0;
|
|
||||||
max = c + ZOOMLIMIT/2.0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (event.ShiftDown() || event.RightUp()) {
|
else if (event.ShiftDown() || event.RightUp()) {
|
||||||
// Zoom OUT
|
// Zoom OUT
|
||||||
// Zoom out to -1.0...1.0 first, then, and only
|
if (spectral) {
|
||||||
// then, if they click again, allow one more
|
|
||||||
// zoom out.
|
|
||||||
if (spectrumLinear) {
|
|
||||||
if (event.ShiftDown() && event.RightUp()) {
|
if (event.ShiftDown() && event.RightUp()) {
|
||||||
// Zoom out full
|
// Zoom out full
|
||||||
min = 0.0;
|
min = spectrumLinear ? 0.0f : 1.0f;
|
||||||
max = rate/2.;
|
max = halfrate;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Zoom out
|
// Zoom out
|
||||||
c = 0.5*(min+max);
|
|
||||||
l = (c - min);
|
// (Used to zoom out centered at midline, ignoring the click, if linear view.
|
||||||
if(c - 2*l <= 0) {
|
// I think it is better to be consistent. PRL)
|
||||||
min = 0.0;
|
// Center zoom-out at the midline
|
||||||
max = std::min( rate/2., 2. * max);
|
const float middle = // spectrumLinear ? 0.5f :
|
||||||
}
|
1.0f - (mZoomStart - ypos) / (float)height;
|
||||||
else {
|
|
||||||
min = std::max( 0.0f, c - 2*l);
|
min = std::max(spectrumLinear ? 0.0f : 1.0f, scale.PositionToValue(middle - 1.0f));
|
||||||
max = std::min( float(rate)/2, c + 2*l);
|
max = std::min(halfrate, scale.PositionToValue(middle + 1.0f));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(spectrumLog) {
|
// Zoom out to -1.0...1.0 first, then, and only
|
||||||
if (event.ShiftDown() && event.RightUp()) {
|
// then, if they click again, allow one more
|
||||||
// Zoom out full
|
// zoom out.
|
||||||
min = 1.0;
|
if (event.ShiftDown() && event.RightUp()) {
|
||||||
max = rate/2.;
|
// Zoom out full
|
||||||
}
|
min = -1.0;
|
||||||
else {
|
max = 1.0;
|
||||||
// Zoom out
|
|
||||||
float p1;
|
|
||||||
p1 = (mZoomStart - ypos) / (float)height;
|
|
||||||
c = 1.0-p1;
|
|
||||||
double xmin = c - 1.;
|
|
||||||
double xmax = c + 1.;
|
|
||||||
double lmin = log10(min), lmax = log10(max);
|
|
||||||
double d = lmax-lmin;
|
|
||||||
min = std::max(1.0f,float(pow(10, xmin*d+lmin)));
|
|
||||||
max = std::min(rate/2., pow(10, xmax*d+lmin));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (event.ShiftDown() && event.RightUp()) {
|
// Zoom out
|
||||||
// Zoom out full
|
if (min <= -1.0 && max >= 1.0) {
|
||||||
min = -1.0;
|
min = -2.0;
|
||||||
max = 1.0;
|
max = 2.0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Zoom out
|
c = 0.5*(min + max);
|
||||||
if (min <= -1.0 && max >= 1.0) {
|
l = (c - min);
|
||||||
min = -2.0;
|
// limit to +/- 1 range unless already outside that range...
|
||||||
max = 2.0;
|
float minRange = (min < -1) ? -2.0 : -1.0;
|
||||||
}
|
float maxRange = (max > 1) ? 2.0 : 1.0;
|
||||||
else {
|
// and enforce vertical zoom limits.
|
||||||
c = 0.5*(min+max);
|
min = std::min(maxRange - ZOOMLIMIT, std::max(minRange, c - 2 * l));
|
||||||
l = (c - min);
|
max = std::max(minRange + ZOOMLIMIT, std::min(maxRange, c + 2 * l));
|
||||||
// limit to +/- 1 range unless already outside that range...
|
|
||||||
float minRange = (min < -1) ? -2.0 : -1.0;
|
|
||||||
float maxRange = (max > 1) ? 2.0 : 1.0;
|
|
||||||
// and enforce vertical zoom limits.
|
|
||||||
min = std::min(maxRange - ZOOMLIMIT, std::max(minRange, c - 2*l));
|
|
||||||
max = std::max(minRange + ZOOMLIMIT, std::min(maxRange, c + 2*l));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Zoom IN
|
// Zoom IN
|
||||||
float p1;
|
if (spectral) {
|
||||||
if (spectrumLinear) {
|
// Center the zoom-in at the click
|
||||||
c = 0.5*(min+max);
|
const float middle = 1.0f - (mZoomStart - ypos) / (float)height;
|
||||||
// Enforce maximum vertical zoom
|
const float middleValue = scale.PositionToValue(middle);
|
||||||
l = std::max( minBins * binSize, (c - min));
|
|
||||||
|
|
||||||
p1 = (mZoomStart - ypos) / (float)height;
|
min = std::max(spectrumLinear ? 0.0f : 1.0f,
|
||||||
c = (max * (1.0-p1) + min * p1);
|
std::min(middleValue - minBand / 2,
|
||||||
min = std::max( 0.0, c - 0.5*l);
|
scale.PositionToValue(middle - 0.25f)
|
||||||
max = std::min( float(rate)/2, min + l);
|
));
|
||||||
|
max = std::min(halfrate,
|
||||||
|
std::max(middleValue + minBand / 2,
|
||||||
|
scale.PositionToValue(middle + 0.25f)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(spectrumLog) {
|
// Zoom in centered on cursor
|
||||||
p1 = (mZoomStart - ypos) / (float)height;
|
float p1;
|
||||||
c = 1.0-p1;
|
if (min < -1.0 || max > 1.0) {
|
||||||
double xmin = c - 0.25;
|
min = -1.0;
|
||||||
double xmax = c + 0.25;
|
max = 1.0;
|
||||||
double lmin = log10(min), lmax = log10(max);
|
|
||||||
double d = lmax-lmin;
|
|
||||||
min = std::max(1.0f, float(pow(10, xmin*d+lmin)));
|
|
||||||
max = std::min(rate/2., pow(10, xmax*d+lmin));
|
|
||||||
// Enforce vertical zoom limits
|
|
||||||
// done in the linear freq domain for now, but not too far out
|
|
||||||
if(max < min + minBins * binSize)
|
|
||||||
max = min + minBins * binSize;
|
|
||||||
if(max > rate/2.) {
|
|
||||||
max = rate/2.;
|
|
||||||
min = max - minBins * binSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Zoom in centered on cursor
|
c = 0.5*(min + max);
|
||||||
if (min < -1.0 || max > 1.0) {
|
// Enforce maximum vertical zoom
|
||||||
min = -1.0;
|
l = std::max(ZOOMLIMIT, (c - min));
|
||||||
max = 1.0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
c = 0.5*(min+max);
|
|
||||||
// Enforce maximum vertical zoom
|
|
||||||
l = std::max( ZOOMLIMIT, (c - min));
|
|
||||||
|
|
||||||
p1 = (mZoomStart - ypos) / (float)height;
|
p1 = (mZoomStart - ypos) / (float)height;
|
||||||
c = (max * (1.0-p1) + min * p1);
|
c = (max * (1.0 - p1) + min * p1);
|
||||||
min = c - 0.5*l;
|
min = c - 0.5*l;
|
||||||
max = c + 0.5*l;
|
max = c + 0.5*l;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spectrumLinear) {
|
if (spectral) {
|
||||||
SpectrogramSettings &settings = track->GetSpectrogramSettings();
|
if (spectrumLinear) {
|
||||||
settings.SetMinFreq(min);
|
SpectrogramSettings &settings = track->GetSpectrogramSettings();
|
||||||
settings.SetMaxFreq(max);
|
|
||||||
if (partner) {
|
|
||||||
// To do: share memory with reference counting?
|
|
||||||
SpectrogramSettings &settings = partner->GetSpectrogramSettings();
|
|
||||||
settings.SetMinFreq(min);
|
settings.SetMinFreq(min);
|
||||||
settings.SetMaxFreq(max);
|
settings.SetMaxFreq(max);
|
||||||
|
if (partner) {
|
||||||
|
// To do: share memory with reference counting?
|
||||||
|
SpectrogramSettings &settings = partner->GetSpectrogramSettings();
|
||||||
|
settings.SetMinFreq(min);
|
||||||
|
settings.SetMaxFreq(max);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else {
|
||||||
else if(spectrumLog) {
|
SpectrogramSettings &settings = track->GetSpectrogramSettings();
|
||||||
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.SetLogMinFreq(min);
|
||||||
settings.SetLogMaxFreq(max);
|
settings.SetLogMaxFreq(max);
|
||||||
|
if (partner) {
|
||||||
|
// To do: share memory with reference counting?
|
||||||
|
SpectrogramSettings &settings = partner->GetSpectrogramSettings();
|
||||||
|
settings.SetLogMinFreq(min);
|
||||||
|
settings.SetLogMaxFreq(max);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -698,13 +698,11 @@ protected:
|
|||||||
bool maySnap,
|
bool maySnap,
|
||||||
wxInt64 mouseYCoordinate,
|
wxInt64 mouseYCoordinate,
|
||||||
wxInt64 trackTopEdge,
|
wxInt64 trackTopEdge,
|
||||||
int trackHeight,
|
int trackHeight) const;
|
||||||
bool logF) const;
|
|
||||||
wxInt64 FrequencyToPosition(const WaveTrack *wt,
|
wxInt64 FrequencyToPosition(const WaveTrack *wt,
|
||||||
double frequency,
|
double frequency,
|
||||||
wxInt64 trackTopEdge,
|
wxInt64 trackTopEdge,
|
||||||
int trackHeight,
|
int trackHeight) const;
|
||||||
bool logF) const;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum SelectionBoundary {
|
enum SelectionBoundary {
|
||||||
|
@ -15,6 +15,7 @@ Paul Licameli
|
|||||||
|
|
||||||
#include "../Audacity.h"
|
#include "../Audacity.h"
|
||||||
#include "SpectrogramSettings.h"
|
#include "SpectrogramSettings.h"
|
||||||
|
#include "../NumberScale.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <wx/msgdlg.h>
|
#include <wx/msgdlg.h>
|
||||||
@ -23,7 +24,6 @@ Paul Licameli
|
|||||||
#include "../Prefs.h"
|
#include "../Prefs.h"
|
||||||
#include "../RealFFTf.h"
|
#include "../RealFFTf.h"
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
SpectrogramSettings::Globals::Globals()
|
SpectrogramSettings::Globals::Globals()
|
||||||
@ -161,6 +161,10 @@ const wxArrayString &SpectrogramSettings::GetScaleNames()
|
|||||||
// Keep in correspondence with enum SpectrogramSettings::ScaleType:
|
// Keep in correspondence with enum SpectrogramSettings::ScaleType:
|
||||||
theArray.Add(_("Linear"));
|
theArray.Add(_("Linear"));
|
||||||
theArray.Add(_("Logarithmic"));
|
theArray.Add(_("Logarithmic"));
|
||||||
|
theArray.Add(_("Mel"));
|
||||||
|
theArray.Add(_("Bark"));
|
||||||
|
theArray.Add(_("Erb"));
|
||||||
|
theArray.Add(_("Undertone"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return theArray;
|
return theArray;
|
||||||
@ -524,6 +528,59 @@ int SpectrogramSettings::GetFFTLength(bool autocorrelation) const
|
|||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NumberScale SpectrogramSettings::GetScale
|
||||||
|
(double rate, bool bins, bool autocorrelation) const
|
||||||
|
{
|
||||||
|
int minFreq, maxFreq;
|
||||||
|
NumberScaleType type = nstLinear;
|
||||||
|
const int half = GetFFTLength(autocorrelation) / 2;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
case stMel:
|
||||||
|
type = nstMel; break;
|
||||||
|
case stBark:
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NumberScale(type, minFreq, maxFreq,
|
||||||
|
bins ? rate / (2 * half) : 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
bool SpectrogramSettings::SpectralSelectionEnabled() const
|
bool SpectrogramSettings::SpectralSelectionEnabled() const
|
||||||
{
|
{
|
||||||
#ifdef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
#ifdef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
||||||
|
@ -16,6 +16,7 @@ Paul Licameli
|
|||||||
#undef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
#undef SPECTRAL_SELECTION_GLOBAL_SWITCH
|
||||||
|
|
||||||
struct FFTParam;
|
struct FFTParam;
|
||||||
|
class NumberScale;
|
||||||
class SpectrumPrefs;
|
class SpectrumPrefs;
|
||||||
class wxArrayString;
|
class wxArrayString;
|
||||||
|
|
||||||
@ -53,6 +54,10 @@ public:
|
|||||||
enum ScaleType {
|
enum ScaleType {
|
||||||
stLinear,
|
stLinear,
|
||||||
stLogarithmic,
|
stLogarithmic,
|
||||||
|
stMel,
|
||||||
|
stBark,
|
||||||
|
stErb,
|
||||||
|
stUndertone,
|
||||||
|
|
||||||
stNumScaleTypes,
|
stNumScaleTypes,
|
||||||
};
|
};
|
||||||
@ -80,6 +85,10 @@ public:
|
|||||||
void ConvertToEnumeratedWindowSizes();
|
void ConvertToEnumeratedWindowSizes();
|
||||||
void ConvertToActualWindowSizes();
|
void ConvertToActualWindowSizes();
|
||||||
|
|
||||||
|
// If "bins" is false, units are Hz
|
||||||
|
NumberScale SpectrogramSettings::GetScale
|
||||||
|
(double rate, bool bins, bool autocorrelation) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int minFreq;
|
int minFreq;
|
||||||
int maxFreq;
|
int maxFreq;
|
||||||
|
@ -76,6 +76,7 @@ array of Ruler::Label.
|
|||||||
#include "../TimeTrack.h"
|
#include "../TimeTrack.h"
|
||||||
#include "../TrackPanel.h"
|
#include "../TrackPanel.h"
|
||||||
#include "../Menus.h"
|
#include "../Menus.h"
|
||||||
|
#include "../NumberScale.h"
|
||||||
#include "../Prefs.h"
|
#include "../Prefs.h"
|
||||||
#include "../Snap.h"
|
#include "../Snap.h"
|
||||||
|
|
||||||
@ -97,6 +98,7 @@ using std::max;
|
|||||||
//
|
//
|
||||||
|
|
||||||
Ruler::Ruler()
|
Ruler::Ruler()
|
||||||
|
: mpNumberScale(0)
|
||||||
{
|
{
|
||||||
mMin = mHiddenMin = 0.0;
|
mMin = mHiddenMin = 0.0;
|
||||||
mMax = mHiddenMax = 100.0;
|
mMax = mHiddenMax = 100.0;
|
||||||
@ -177,6 +179,8 @@ Ruler::~Ruler()
|
|||||||
delete[] mMinorLabels;
|
delete[] mMinorLabels;
|
||||||
if (mMinorMinorLabels)
|
if (mMinorMinorLabels)
|
||||||
delete[] mMinorMinorLabels;
|
delete[] mMinorMinorLabels;
|
||||||
|
|
||||||
|
delete mpNumberScale;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Ruler::SetTwoTone(bool twoTone)
|
void Ruler::SetTwoTone(bool twoTone)
|
||||||
@ -319,6 +323,23 @@ void Ruler::SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxF
|
|||||||
Invalidate();
|
Invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Ruler::SetNumberScale(const NumberScale *pScale)
|
||||||
|
{
|
||||||
|
if (!pScale) {
|
||||||
|
if (mpNumberScale) {
|
||||||
|
delete mpNumberScale;
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!mpNumberScale || *mpNumberScale != *pScale) {
|
||||||
|
delete mpNumberScale;
|
||||||
|
mpNumberScale = new NumberScale(*pScale);
|
||||||
|
Invalidate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Ruler::OfflimitsPixels(int start, int end)
|
void Ruler::OfflimitsPixels(int start, int end)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -1165,13 +1186,17 @@ void Ruler::Update(TimeTrack* timetrack)// Envelope *speedEnv, long minSpeed, lo
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// log case
|
// log case
|
||||||
mDigits=2; //TODO: implement dynamic digit computation
|
|
||||||
|
NumberScale numberScale(mpNumberScale
|
||||||
|
? *mpNumberScale
|
||||||
|
: NumberScale(nstLogarithmic, mMin, mMax, 1.0f)
|
||||||
|
);
|
||||||
|
|
||||||
|
mDigits=2; //TODO: implement dynamic digit computation
|
||||||
double loLog = log10(mMin);
|
double loLog = log10(mMin);
|
||||||
double hiLog = log10(mMax);
|
double hiLog = log10(mMax);
|
||||||
double scale = mLength/(hiLog - loLog);
|
|
||||||
int loDecade = (int) floor(loLog);
|
int loDecade = (int) floor(loLog);
|
||||||
|
|
||||||
int pos;
|
|
||||||
double val;
|
double val;
|
||||||
double startDecade = pow(10., (double)loDecade);
|
double startDecade = pow(10., (double)loDecade);
|
||||||
|
|
||||||
@ -1179,12 +1204,12 @@ void Ruler::Update(TimeTrack* timetrack)// Envelope *speedEnv, long minSpeed, lo
|
|||||||
double decade = startDecade;
|
double decade = startDecade;
|
||||||
double delta=hiLog-loLog, steps=fabs(delta);
|
double delta=hiLog-loLog, steps=fabs(delta);
|
||||||
double step = delta>=0 ? 10 : 0.1;
|
double step = delta>=0 ? 10 : 0.1;
|
||||||
double rMin=wxMin(mMin, mMax), rMax=wxMax(mMin, mMax);
|
double rMin=std::min(mMin, mMax), rMax=std::max(mMin, mMax);
|
||||||
for(i=0; i<=steps; i++)
|
for(i=0; i<=steps; i++)
|
||||||
{ // if(i!=0)
|
{ // if(i!=0)
|
||||||
{ val = decade;
|
{ val = decade;
|
||||||
if(val > rMin && val < rMax) {
|
if(val >= rMin && val < rMax) {
|
||||||
pos = (int)(((log10(val) - loLog)*scale)+0.5);
|
const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
|
||||||
Tick(pos, val, true, false);
|
Tick(pos, val, true, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1204,7 +1229,7 @@ void Ruler::Update(TimeTrack* timetrack)// Envelope *speedEnv, long minSpeed, lo
|
|||||||
for(j=start; j!=end; j+=mstep) {
|
for(j=start; j!=end; j+=mstep) {
|
||||||
val = decade * j;
|
val = decade * j;
|
||||||
if(val >= rMin && val < rMax) {
|
if(val >= rMin && val < rMax) {
|
||||||
pos = (int)(((log10(val) - loLog)*scale)+0.5);
|
const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
|
||||||
Tick(pos, val, false, true);
|
Tick(pos, val, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1219,13 +1244,16 @@ void Ruler::Update(TimeTrack* timetrack)// Envelope *speedEnv, long minSpeed, lo
|
|||||||
{ start=100; end= 10; mstep=-1;
|
{ start=100; end= 10; mstep=-1;
|
||||||
}
|
}
|
||||||
steps++;
|
steps++;
|
||||||
for(i=0; i<=steps; i++) {
|
for (i = 0; i <= steps; i++) {
|
||||||
for(int f=start; f!=int(end); f+=mstep) {
|
// PRL: Bug1038. Don't label 1.6, rounded, as a duplicate tick for "2"
|
||||||
if (int(f/10)!=f/10.0f) {
|
if (!(mFormat == IntFormat && decade < 10.0)) {
|
||||||
val = decade * f/10;
|
for (int f = start; f != int(end); f += mstep) {
|
||||||
if(val >= rMin && val < rMax) {
|
if (int(f / 10) != f / 10.0f) {
|
||||||
pos = (int)(((log10(val) - loLog)*scale)+0.5);
|
val = decade * f / 10;
|
||||||
Tick(pos, val, false, false);
|
if (val >= rMin && val < rMax) {
|
||||||
|
const int pos(0.5 + mLength * numberScale.ValueToPosition(val));
|
||||||
|
Tick(pos, val, false, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1904,8 +1932,8 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
|||||||
TrackPanel *tp = mProject->GetTrackPanel();
|
TrackPanel *tp = mProject->GetTrackPanel();
|
||||||
int mousePosX, width, height;
|
int mousePosX, width, height;
|
||||||
tp->GetTracksUsableArea(&width, &height);
|
tp->GetTracksUsableArea(&width, &height);
|
||||||
mousePosX = wxMax(evt.GetX(), tp->GetLeftOffset());
|
mousePosX = std::max(evt.GetX(), tp->GetLeftOffset());
|
||||||
mousePosX = wxMin(mousePosX, tp->GetLeftOffset() + width - 1);
|
mousePosX = std::min(mousePosX, tp->GetLeftOffset() + width - 1);
|
||||||
|
|
||||||
bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
|
bool isWithinStart = IsWithinMarker(mousePosX, mOldPlayRegionStart);
|
||||||
bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
|
bool isWithinEnd = IsWithinMarker(mousePosX, mOldPlayRegionEnd);
|
||||||
@ -1920,7 +1948,7 @@ void AdornedRulerPanel::OnMouseEvents(wxMouseEvent &evt)
|
|||||||
mLastMouseX = mousePosX;
|
mLastMouseX = mousePosX;
|
||||||
mQuickPlayPos = Pos2Time(mousePosX);
|
mQuickPlayPos = Pos2Time(mousePosX);
|
||||||
// If not looping, restrict selection to end of project
|
// If not looping, restrict selection to end of project
|
||||||
if (!evt.ShiftDown()) mQuickPlayPos = wxMin(t1, mQuickPlayPos);
|
if (!evt.ShiftDown()) mQuickPlayPos = std::min(t1, mQuickPlayPos);
|
||||||
|
|
||||||
|
|
||||||
if (evt.Leaving()) {
|
if (evt.Leaving()) {
|
||||||
@ -2487,7 +2515,7 @@ void AdornedRulerPanel::DrawQuickPlayIndicator(wxDC * dc, bool clear)
|
|||||||
TrackPanel *tp = mProject->GetTrackPanel();
|
TrackPanel *tp = mProject->GetTrackPanel();
|
||||||
wxClientDC cdc(tp);
|
wxClientDC cdc(tp);
|
||||||
|
|
||||||
double latestEnd = wxMax(mProject->GetTracks()->GetEndTime(), mProject->GetSel1());
|
double latestEnd = std::max(mProject->GetTracks()->GetEndTime(), mProject->GetSel1());
|
||||||
if (clear || (mQuickPlayPos >= latestEnd)) {
|
if (clear || (mQuickPlayPos >= latestEnd)) {
|
||||||
tp->TrackPanel::DrawQuickPlayIndicator(cdc, -1);
|
tp->TrackPanel::DrawQuickPlayIndicator(cdc, -1);
|
||||||
return;
|
return;
|
||||||
|
@ -23,6 +23,7 @@ class ViewInfo;
|
|||||||
class AudacityProject;
|
class AudacityProject;
|
||||||
class TimeTrack;
|
class TimeTrack;
|
||||||
class SnapManager;
|
class SnapManager;
|
||||||
|
class NumberScale;
|
||||||
|
|
||||||
class AUDACITY_DLL_API Ruler {
|
class AUDACITY_DLL_API Ruler {
|
||||||
public:
|
public:
|
||||||
@ -98,6 +99,9 @@ class AUDACITY_DLL_API Ruler {
|
|||||||
// Good defaults are provided, but you can override here
|
// Good defaults are provided, but you can override here
|
||||||
void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont);
|
void SetFonts(const wxFont &minorFont, const wxFont &majorFont, const wxFont &minorMinorFont);
|
||||||
|
|
||||||
|
// Copies *pScale if it is not NULL
|
||||||
|
void SetNumberScale(const NumberScale *pScale);
|
||||||
|
|
||||||
// The ruler will not draw text within this (pixel) range.
|
// The ruler will not draw text within this (pixel) range.
|
||||||
// Use this if you have another graphic object obscuring part
|
// Use this if you have another graphic object obscuring part
|
||||||
// of the ruler's area. The values start and end are interpreted
|
// of the ruler's area. The values start and end are interpreted
|
||||||
@ -227,6 +231,8 @@ private:
|
|||||||
bool mTwoTone;
|
bool mTwoTone;
|
||||||
bool mUseZoomInfo;
|
bool mUseZoomInfo;
|
||||||
int mLeftOffset;
|
int mLeftOffset;
|
||||||
|
|
||||||
|
NumberScale *mpNumberScale;
|
||||||
};
|
};
|
||||||
|
|
||||||
class AUDACITY_DLL_API RulerPanel : public wxPanel {
|
class AUDACITY_DLL_API RulerPanel : public wxPanel {
|
||||||
|
@ -533,6 +533,7 @@
|
|||||||
<ClInclude Include="..\..\..\src\import\MultiFormatReader.h" />
|
<ClInclude Include="..\..\..\src\import\MultiFormatReader.h" />
|
||||||
<ClInclude Include="..\..\..\src\import\SpecPowerMeter.h" />
|
<ClInclude Include="..\..\..\src\import\SpecPowerMeter.h" />
|
||||||
<ClInclude Include="..\..\..\src\ModuleManager.h" />
|
<ClInclude Include="..\..\..\src\ModuleManager.h" />
|
||||||
|
<ClInclude Include="..\..\..\src\NumberScale.h" />
|
||||||
<ClInclude Include="..\..\..\src\prefs\SpectrogramSettings.h" />
|
<ClInclude Include="..\..\..\src\prefs\SpectrogramSettings.h" />
|
||||||
<ClInclude Include="..\..\..\src\RevisionIdent.h" />
|
<ClInclude Include="..\..\..\src\RevisionIdent.h" />
|
||||||
<ClInclude Include="..\..\..\src\SelectedRegion.h" />
|
<ClInclude Include="..\..\..\src\SelectedRegion.h" />
|
||||||
|
@ -1685,6 +1685,9 @@
|
|||||||
<ClInclude Include="..\..\..\src\prefs\SpectrogramSettings.h">
|
<ClInclude Include="..\..\..\src\prefs\SpectrogramSettings.h">
|
||||||
<Filter>src/prefs</Filter>
|
<Filter>src/prefs</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\src\NumberScale.h">
|
||||||
|
<Filter>src</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Image Include="..\..\audacity.ico">
|
<Image Include="..\..\audacity.ico">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user