1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-18 17:10:05 +02:00

Use class NumberScale in TrackArtist and spectral selection, abstracting...

... the details of mapping to and from pixel height
This commit is contained in:
Paul Licameli 2015-06-16 12:46:55 -04:00
parent 045828d744
commit 43b7df701b
5 changed files with 173 additions and 244 deletions

View File

@ -168,6 +168,7 @@ audio tracks.
#include "AColor.h"
#include "BlockFile.h"
#include "Envelope.h"
#include "NumberScale.h"
#include "Track.h"
#include "WaveTrack.h"
#include "LabelTrack.h"
@ -2036,7 +2037,6 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
const int display = track->GetDisplay();
const bool autocorrelation = (WaveTrack::PitchDisplay == display);
const bool logF = settings.scaleType == SpectrogramSettings::stLogarithmic;
enum { DASH_LENGTH = 10 /* pixels */ };
@ -2115,15 +2115,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
scaleType == SpectrogramSettings::stLinear
? settings.GetMaxFreq(rate) : settings.GetLogMaxFreq(rate);
float minBin = ((double)minFreq / binUnit);
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;
const NumberScale numberScale(settings.GetScale(rate, true, autocorrelation));
#ifdef EXPERIMENTAL_FFT_Y_GRID
const float
@ -2204,34 +2196,36 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
int *indexes = new int[maxTableSize];
#endif //EXPERIMENTAL_FIND_NOTES
for (int xx = 0; xx < hiddenMid.width; ++xx)
{
if (!logF) {
for (int xx = 0; xx < hiddenMid.width; ++xx) {
NumberScale::Iterator it = numberScale.begin(mid.height);
float nextBin = std::max(0.0f, std::min(float(half - 1), *it));
for (int yy = 0; yy < hiddenMid.height; ++yy) {
float bin0 = float(yy) * binPerPx + minBin;
float bin1 = float(yy + 1) * binPerPx + minBin;
const float bin = nextBin;
nextBin = std::max(0.0f, std::min(float(half - 1), *++it));
if (settings.scaleType != SpectrogramSettings::stLogarithmic) {
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;
}
}
else {
// Do we need this legacy experiment still?
#ifdef EXPERIMENTAL_FIND_NOTES
int maximas=0;
const int x0 = half * xx;
int maximas = 0;
const int x0 = half * x;
if (fftFindNotes) {
for (int i = maxTableSize-1; i >= 0; i--)
indexes[i]=-1;
for (int i = maxTableSize - 1; i >= 0; i--)
indexes[i] = -1;
// Build a table of (most) values, put the index in it.
for (int i = int(i0); i < int(i1); i++) {
float freqi=freq[x0 + int(i)];
int value=int((freqi+gain+range)/range*(maxTableSize-1));
float freqi = freq[x0 + int(i)];
int value = int((freqi + gain + range) / range*(maxTableSize - 1));
if (value < 0)
value=0;
value = 0;
if (value >= maxTableSize)
value=maxTableSize-1;
indexes[value]=i;
value = maxTableSize - 1;
indexes[value] = i;
}
// Build from the indices an array of maxima.
for (int i = maxTableSize - 1; i >= 0; i--) {
@ -2262,77 +2256,64 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
#define f2pix(f) (logf(f)-lmins)/(lmaxs-lmins)*hiddenMid.height
// Possibly quantize the maxima frequencies and create the pixel block limits.
for (int i=0; i < maximas; i++) {
int index=maxima[i];
for (int i = 0; i < maximas; i++) {
int index = maxima[i];
float f = float(index)*bin2f;
if (findNotesQuantize)
{ f = expf(int(log(f/440)/log2*12-0.5)/12.0f*log2)*440;
{
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;
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;
float f1 = expf((log(f / 440) / log2 * 24 + 1) / 24.0f*log2) * 440;
maxima1[i] = f2pix(f1);
}
}
int it=0;
int oldBin0=-1;
int it = 0;
int oldBin0 = -1;
bool inMaximum = false;
#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;
#ifdef EXPERIMENTAL_FIND_NOTES
if (fftFindNotes) {
if (it < maximas) {
float i0=maxima0[it];
float i0 = maxima0[it];
if (yy >= i0)
inMaximum = true;
if (inMaximum) {
float i1=maxima1[it];
if (yy+1 <= i1) {
value=findValue(freq + x0, bin0, bin1, half, autocorrelation, gain, range);
float i1 = maxima1[it];
if (yy + 1 <= i1) {
value = findValue(freq + x0, bin, nextBin, half, autocorrelation, gain, range);
if (value < findNotesMinA)
value = minColor;
} else {
}
else {
it++;
inMaximum = false;
value = minColor;
}
} else {
}
else {
value = minColor;
}
} else
}
else
value = minColor;
} else
}
else
#endif //EXPERIMENTAL_FIND_NOTES
{
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;
yy2 = yy2_base;
} // logF
} // each yy
} // is logF
} // each xx
} // updating cache
float selBinLo = freqLo / binUnit;
@ -2384,66 +2365,26 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
(zoomInfo.PositionToTime(xx + 1, -leftOffset) - tOffset)
);
// TODO: The logF and non-logF case are very similar.
// They should be merged and simplified.
if (!logF)
{
NumberScale::Iterator it = numberScale.begin(mid.height);
float nextBin = std::max(0.0f, std::min(float(half - 1), *it));
for (int yy = 0; yy < hiddenMid.height; ++yy) {
float bin0 = float(yy) * binPerPx + minBin;
float bin1 = float(yy + 1) * binPerPx + minBin;
const float bin = nextBin;
nextBin = std::max(0.0f, std::min(float(half - 1), *++it));
// For spectral selection, determine what colour
// set to use. We use a darker selection if
// in both spectral range and time range.
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);
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,
selected =
ChooseColorSet(bin, nextBin, 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)
? findValue(uncached, bin, nextBin, half, autocorrelation, gain, range)
: clip->mSpecPxCache->values[correctedX * hiddenMid.height + yy];
unsigned char rv, gv, bv;
GetColorGradient(value, selected, isGrayscale, &rv, &gv, &bv);
#ifdef EXPERIMENTAL_FFT_Y_GRID
@ -2458,11 +2399,8 @@ void TrackArtist::DrawClipSpectrum(WaveTrackCache &waveTrackCache,
data[px++] = rv;
data[px++] = gv;
data[px] = bv;
yy2 = yy2_base;
}
}
}
} // each yy
} // each xx
wxBitmap converted = wxBitmap(*image);

View File

@ -201,6 +201,7 @@ is time to refresh some aspect of the screen.
#include "MixerBoard.h"
#include "NoteTrack.h"
#include "NumberScale.h"
#include "Prefs.h"
#include "Project.h"
#include "Snap.h"
@ -1802,22 +1803,15 @@ void TrackPanel::SetCursorAndTipWhenInLabelTrack( LabelTrack * pLT,
namespace {
// 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 &&
pTrack->GetKind() == Track::Wave) {
const WaveTrack *const wt = static_cast<const WaveTrack*>(pTrack);
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
const int display = wt->GetDisplay();
if (pLogf) {
const bool logF =
settings.scaleType == SpectrogramSettings::stLogarithmic;
*pLogf = logF;
}
return (display == WaveTrack::Spectrum) && settings.SpectralSelectionEnabled();
}
else {
if (pLogf)
*pLogf = false;
return false;
}
}
@ -1941,9 +1935,8 @@ void TrackPanel::SetCursorAndTipWhenSelectTool( Track * t,
const bool bShiftDown = event.ShiftDown();
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
bool logF;
if ( (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER) &&
isSpectralSelectionTrack(t, &logF)) {
isSpectralSelectionTrack(t)) {
// Not shift-down, but center frequency snapping toggle is on
*ppTip = _("Click and drag to set frequency bandwidth.");
*ppCursor = mEnvelopeCursor;
@ -2679,9 +2672,8 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
// preferences now
if (mAdjustSelectionEdges) {
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
bool logF;
if (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER &&
isSpectralSelectionTrack(pTrack, &logF)) {
isSpectralSelectionTrack(pTrack)) {
// Ignore whether we are inside the time selection.
// Exit center-snapping, start dragging the width.
mFreqSelMode = FREQ_SEL_PINNED_CENTER;
@ -3072,10 +3064,9 @@ void TrackPanel::MoveSnappingFreqSelection (int mouseYCoordinate,
int trackTopEdge,
int trackHeight, Track *pTrack)
{
bool logF;
if (pTrack &&
pTrack->GetSelected() &&
isSpectralSelectionTrack(pTrack, &logF)) {
isSpectralSelectionTrack(pTrack)) {
WaveTrack *const wt = static_cast<WaveTrack*>(pTrack);
// PRL:
// 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 frequency =
PositionToFrequency(wt, false, mouseYCoordinate,
trackTopEdge, trackHeight, logF);
trackTopEdge, trackHeight);
const double snappedFrequency =
mFrequencySnapper->FindPeak(frequency, NULL);
const double maxRatio = findMaxRatio(snappedFrequency, rate);
@ -3118,13 +3109,12 @@ void TrackPanel::StartFreqSelection (int mouseYCoordinate, int trackTopEdge,
mFreqSelMode = FREQ_SEL_INVALID;
mFreqSelPin = SelectedRegion::UndefinedFrequency;
bool logF;
if (isSpectralSelectionTrack(pTrack, &logF)) {
if (isSpectralSelectionTrack(pTrack)) {
mFreqSelTrack = static_cast<WaveTrack*>(pTrack);
mFreqSelMode = FREQ_SEL_FREE;
mFreqSelPin =
PositionToFrequency(mFreqSelTrack, false, mouseYCoordinate,
trackTopEdge, trackHeight, logF);
trackTopEdge, trackHeight);
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.
const WaveTrack* wt = mFreqSelTrack;
const bool logF =
wt->GetSpectrogramSettings().scaleType == SpectrogramSettings::stLogarithmic;
const double rate = wt->GetRate();
const double frequency =
PositionToFrequency(wt, true, mouseYCoordinate,
trackTopEdge, trackHeight, logF);
trackTopEdge, trackHeight);
// Dragging center?
if (mFreqSelMode == FREQ_SEL_DRAG_CENTER) {
@ -3527,8 +3515,7 @@ double TrackPanel::PositionToFrequency(const WaveTrack *wt,
bool maySnap,
wxInt64 mouseYCoordinate,
wxInt64 trackTopEdge,
int trackHeight,
bool logF) const
int trackHeight) const
{
const double rate = wt->GetRate();
@ -3540,57 +3527,23 @@ double TrackPanel::PositionToFrequency(const WaveTrack *wt,
trackTopEdge + trackHeight - mouseYCoordinate < FREQ_SNAP_DISTANCE)
return -1;
const SpectrogramSettings &settings = wt->GetSpectrogramSettings();
const NumberScale numberScale(settings.GetScale(rate, false, false));
const double p = double(mouseYCoordinate - trackTopEdge) / trackHeight;
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;
}
return numberScale.PositionToValue(1.0 - p);
}
/// Converts a frequency to screen y position.
wxInt64 TrackPanel::FrequencyToPosition(const WaveTrack *wt,
double frequency,
wxInt64 trackTopEdge,
int trackHeight,
bool logF) const
int trackHeight) const
{
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 NumberScale numberScale(settings.GetScale(rate, false, false));
const float p = numberScale.ValueToPosition(frequency);
return trackTopEdge + wxInt64((1.0 - p) * trackHeight);
}
#endif
@ -3671,18 +3624,17 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
bool chooseTime = true;
bool chooseBottom = true;
bool chooseCenter = false;
bool logF;
// Consider adjustment of frequencies only if mouse is
// within the time boundaries
if (!mViewInfo->selectedRegion.isPoint() &&
t0 <= selend && selend < t1 &&
isSpectralSelectionTrack(pTrack, &logF)) {
isSpectralSelectionTrack(pTrack)) {
const WaveTrack *const wt = static_cast<const WaveTrack*>(pTrack);
const wxInt64 bottomSel = (f0 >= 0)
? FrequencyToPosition(wt, f0, rect.y, rect.height, logF)
? FrequencyToPosition(wt, f0, rect.y, rect.height)
: rect.y + rect.height;
const wxInt64 topSel = (f1 >= 0)
? FrequencyToPosition(wt, f1, rect.y, rect.height, logF)
? FrequencyToPosition(wt, f1, rect.y, rect.height)
: rect.y;
wxInt64 signedBottomDist = int(event.m_y - bottomSel);
wxInt64 verticalDist = abs(signedBottomDist);
@ -3700,7 +3652,7 @@ bool mayDragWidth, bool onlyWithinSnapDistance,
#endif
) {
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));
if (centerDist < verticalDist)
chooseCenter = true, verticalDist = centerDist,

View File

@ -698,13 +698,11 @@ protected:
bool maySnap,
wxInt64 mouseYCoordinate,
wxInt64 trackTopEdge,
int trackHeight,
bool logF) const;
int trackHeight) const;
wxInt64 FrequencyToPosition(const WaveTrack *wt,
double frequency,
wxInt64 trackTopEdge,
int trackHeight,
bool logF) const;
int trackHeight) const;
#endif
enum SelectionBoundary {

View File

@ -15,6 +15,7 @@ Paul Licameli
#include "../Audacity.h"
#include "SpectrogramSettings.h"
#include "../NumberScale.h"
#include <algorithm>
#include <wx/msgdlg.h>
@ -23,7 +24,6 @@ Paul Licameli
#include "../Prefs.h"
#include "../RealFFTf.h"
#include <algorithm>
#include <cmath>
SpectrogramSettings::Globals::Globals()
@ -524,6 +524,42 @@ int SpectrogramSettings::GetFFTLength(bool autocorrelation) const
;
}
NumberScale SpectrogramSettings::GetScale
(double rate, bool bins, bool autocorrelation) const
{
int minFreq, maxFreq;
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;
}
switch (scaleType) {
default:
wxASSERT(false);
case stLinear:
minFreq = GetMinFreq(rate);
maxFreq = GetMaxFreq(rate);
break;
case stLogarithmic:
minFreq = GetLogMinFreq(rate);
maxFreq = GetLogMaxFreq(rate);
break;
}
const int half = GetFFTLength(autocorrelation) / 2;
return NumberScale(type, minFreq, maxFreq,
bins ? rate / (2 * half) : 1.0f);
}
bool SpectrogramSettings::SpectralSelectionEnabled() const
{
#ifdef SPECTRAL_SELECTION_GLOBAL_SWITCH

View File

@ -16,6 +16,7 @@ Paul Licameli
#undef SPECTRAL_SELECTION_GLOBAL_SWITCH
struct FFTParam;
class NumberScale;
class SpectrumPrefs;
class wxArrayString;
@ -80,6 +81,10 @@ public:
void ConvertToEnumeratedWindowSizes();
void ConvertToActualWindowSizes();
// If "bins" is false, units are Hz
NumberScale SpectrogramSettings::GetScale
(double rate, bool bins, bool autocorrelation) const;
private:
int minFreq;
int maxFreq;