1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-15 23:59:37 +02:00

Paul Licameli's Spectral Editing Patch.

This relies on three new nyquist scripts to actually do the editing.  The peak-snapping code in FrequencyWindow has been extracted into a new class, SpectrumAnalyst, to provide peak-snapping in spectrogram too.
This commit is contained in:
james.k.crook@gmail.com 2014-10-18 14:19:38 +00:00
parent b84fdb82e1
commit 37608c2290
28 changed files with 1342 additions and 279 deletions

View File

@ -0,0 +1,33 @@
;nyquist plug-in
;version 3
;type process
;name "Spectral edit multi tool"
;action "Calculating..."
(defun wet (sig)
(cond
((not (or *f0* *f1*)) (throw 'error-message "Please select frequencies"))
((not *f0*) (highpass2 sig *f1*))
((not *f1*) (lowpass2 sig *f0*))
(t (if (= *f0* *f1*)
(throw 'error-message "Band width is undefined")
(let*
((fc (sqrt (* *f0* *f1*)))
(width (abs (- *f1* *f0*)))
(q (/ fc width)))
(notch2 sig fc q))))))
(defun result (sig)
(let*
((tn (truncate len))
(rate (snd-srate sig))
(transition (truncate (* 0.01 rate)))
(t1 (min transition (/ tn 2)))
(t2 (max (- tn transition) (/ tn 2)))
(breakpoints (list t1 1.0 t2 1.0 tn))
(env (snd-pwl 0.0 rate breakpoints)))
(sum (prod env (wet sig)) (prod (diff 1.0 env) sig))))
(catch 'error-message
(multichan-expand #'result s))

View File

@ -0,0 +1,32 @@
;nyquist plug-in
;version 3
;type process
;name "Spectral edit parametric EQ"
;action "Calculating..."
;control control-gain "Gain (dB)" real "" 0 -24 24
(defun wet (sig gain)
(cond
((not (or *f0* *f1*)) (throw 'debug-message "Please select frequencies"))
((not *f0*) (throw 'debug-message "Bottom frequency is undefined"))
((not *f1*) (throw 'debug-message "Top frequency is undefined"))
(t (let*
((fc (sqrt (* *f0* *f1*)))
(width-octaves (/ (s-log (/ *f1* *f0*)) (s-log 2.0))))
(eq-band sig fc gain (/ width-octaves 2))))))
(defun result (sig)
(let*
((tn (truncate len))
(rate (snd-srate sig))
(transition (truncate (* 0.01 rate)))
(t1 (min transition (/ tn 2)))
(t2 (max (- tn transition) (/ tn 2)))
(breakpoints (list t1 1.0 t2 1.0 tn))
(env (snd-pwl 0.0 rate breakpoints)))
(sum (prod env (wet sig control-gain)) (prod (diff 1.0 env) sig))))
(catch 'debug-message
(multichan-expand #'result s))

View File

@ -0,0 +1,36 @@
;nyquist plug-in
;version 3
;type process
;name "Spectral edit shelves"
;action "Calculating..."
;control control-gain "Gain (dB)" real "" 0 -24 24
(defun mid-shelf (sig lf hf gain)
"Combines high shelf and low shelf filters"
(let* ((invg (- gain)))
(scale (db-to-linear gain)
(eq-highshelf (eq-lowshelf sig lf invg)
hf invg))))
(defun wet (sig gain)
(cond
((not (or *f0* *f1*)) (throw 'debug-message "Please select frequencies"))
((not *f0*) (eq-lowshelf sig *f1* gain))
((not *f1*) (eq-highshelf sig *f0* gain))
(t (mid-shelf sig *f0* *f1* gain))))
(defun result (sig)
(let*
((tn (truncate len))
(rate (snd-srate sig))
(transition (truncate (* 0.01 rate)))
(t1 (min transition (/ tn 2)))
(t2 (max (- tn transition) (/ tn 2)))
(breakpoints (list t1 1.0 t2 1.0 tn))
(env (snd-pwl 0.0 rate breakpoints)))
(sum (prod env (wet sig control-gain)) (prod (diff 1.0 env) sig))))
(catch 'debug-message
(multichan-expand #'result s))

View File

@ -570,14 +570,14 @@ void AColor::DarkMIDIChannel(wxDC * dc, int channel /* 1 - 16 */ )
bool AColor::gradient_inited = 0;
unsigned char AColor::gradient_pre[2][2][gradientSteps][3];
unsigned char AColor::gradient_pre[ColorGradientTotal][2][gradientSteps][3];
void AColor::PreComputeGradient() {
{
if (!gradient_inited) {
gradient_inited = 1;
for (int selected = 0; selected <= 1; selected++)
for (int selected = 0; selected < ColorGradientTotal; selected++)
for (int grayscale = 0; grayscale <= 1; grayscale++) {
float r, g, b;
@ -608,10 +608,24 @@ void AColor::PreComputeGradient() {
b = (gradient[left][2] * lweight) + (gradient[right][2] * rweight);
}
if (selected) {
switch (selected) {
case ColorGradientUnselected:
// not dimmed
break;
case ColorGradientTimeSelected:
// partly dimmed
r *= 0.88f;
g *= 0.88f;
b *= 0.992f;
break;
case ColorGradientTimeAndFrequencySelected:
// fully dimmed
r *= 0.77f;
g *= 0.77f;
b *= 0.885f;
break;
}
gradient_pre[selected][grayscale][i][0] = (unsigned char) (255 * r);
gradient_pre[selected][grayscale][i][1] = (unsigned char) (255 * g);

View File

@ -22,6 +22,15 @@ class wxRect;
class AColor {
public:
enum ColorGradientChoice {
ColorGradientUnselected = 0,
ColorGradientTimeSelected,
ColorGradientTimeAndFrequencySelected,
ColorGradientTotal // keep me last
};
static void Init();
static void ReInit();
@ -97,7 +106,7 @@ class AColor {
static bool gradient_inited;
static const int gradientSteps = 512;
static unsigned char gradient_pre[2][2][gradientSteps][3];
static unsigned char gradient_pre[ColorGradientTotal][2][gradientSteps][3];
private:
static wxPen sparePen;
@ -107,7 +116,7 @@ class AColor {
};
inline void GetColorGradient(float value,
bool selected,
AColor::ColorGradientChoice selected,
bool grayscale,
unsigned char *red,
unsigned char *green, unsigned char *blue) {

View File

@ -102,6 +102,9 @@
// Won't build on Fedora 17 or Windows VC++, per http://bugzilla.audacityteam.org/show_bug.cgi?id=539.
//#define EXPERIMENTAL_OD_FFMPEG 1
// Paul Licameli (PRL) 5 Oct 2014
#define EXPERIMENTAL_SPECTRAL_EDITING
// Philip Van Baren 01 July 2009
// Replace RealFFT() and PowerSpectrum function to use (faster) RealFFTf function
#define EXPERIMENTAL_USE_REALFFTF

View File

@ -107,12 +107,23 @@ BEGIN_EVENT_TABLE(FreqWindow, wxDialog)
EVT_CHECKBOX(GridOnOffID, FreqWindow::OnGridOnOff)
END_EVENT_TABLE()
SpectrumAnalyst::SpectrumAnalyst()
: mAlg(Spectrum)
, mRate(0.0)
, mWindowSize(0)
{
}
SpectrumAnalyst::~SpectrumAnalyst()
{
}
FreqWindow::FreqWindow(wxWindow * parent, wxWindowID id,
const wxString & title,
const wxPoint & pos):
wxDialog(parent, id, title, pos, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX),
mData(NULL), mProcessed(NULL), mBitmap(NULL)
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxMAXIMIZE_BOX),
mData(NULL), mBitmap(NULL), mAnalyst(new SpectrumAnalyst())
{
mMouseX = 0;
mMouseY = 0;
@ -132,7 +143,11 @@ FreqWindow::FreqWindow(wxWindow * parent, wxWindowID id,
gPrefs->Read(wxT("/FreqWindow/DrawGrid"), &mDrawGrid, true);
gPrefs->Read(wxT("/FreqWindow/SizeChoice"), &mSize, 2);
gPrefs->Read(wxT("/FreqWindow/AlgChoice"), &mAlg, 0);
int alg;
gPrefs->Read(wxT("/FreqWindow/AlgChoice"), (&alg), 0);
mAlg = static_cast<SpectrumAnalyst::Algorithm>(alg);
gPrefs->Read(wxT("/FreqWindow/FuncChoice"), &mFunc, 3);
gPrefs->Read(wxT("/FreqWindow/AxisChoice"), &mAxis, 0);
gPrefs->Read(wxT("/GUI/EnvdBRange"), &dBRange, ENV_DB_RANGE);
@ -214,7 +229,7 @@ FreqWindow::FreqWindow(wxWindow * parent, wxWindowID id,
mAxisChoice->SetSelection(mAxis);
// Log-frequency axis works for spectrum plots only.
if (mAlg != 0) {
if (mAlg != SpectrumAnalyst::Spectrum) {
mAxis = 0;
mAxisChoice->Disable();
}
@ -381,8 +396,6 @@ FreqWindow::~FreqWindow()
delete[] mData;
if (mBuffer)
delete[] mBuffer;
if (mProcessed)
delete[] mProcessed;
}
void FreqWindow::GetAudio()
@ -489,7 +502,7 @@ void FreqWindow::DrawPlot()
memDC.SetBrush(*wxWHITE_BRUSH);
memDC.DrawRectangle(r);
if (!mProcessed) {
if (0 == mAnalyst->GetProcessedSize()) {
if (mData && mDataLen < mWindowSize)
memDC.DrawText(_("Not enough data selected."), r.x + 5, r.y + 5);
@ -498,7 +511,8 @@ void FreqWindow::DrawPlot()
float yTotal = (mYMax - mYMin);
int alg = mAlgChoice->GetSelection();
SpectrumAnalyst::Algorithm alg =
SpectrumAnalyst::Algorithm(mAlgChoice->GetSelection());
int i;
@ -506,7 +520,7 @@ void FreqWindow::DrawPlot()
// Set up y axis ruler
if (alg == 0) {
if (alg == SpectrumAnalyst::Spectrum) {
vRuler->ruler.SetUnits(_("dB"));
vRuler->ruler.SetFormat(Ruler::LinearDBFormat);
} else {
@ -531,7 +545,7 @@ void FreqWindow::DrawPlot()
float xMin, xMax, xRatio, xStep;
if (alg == 0) {
if (alg == SpectrumAnalyst::Spectrum) {
xMin = mRate / mWindowSize;
xMax = mRate / 2;
xRatio = xMax / xMin;
@ -548,7 +562,7 @@ void FreqWindow::DrawPlot()
hRuler->ruler.SetUnits(_("Hz"));
} else {
xMin = 0;
xMax = mProcessedSize / mRate;
xMax = mAnalyst->GetProcessedSize() / mRate;
xStep = (xMax - xMin) / width;
hRuler->ruler.SetLog(false);
hRuler->ruler.SetUnits(_("s"));
@ -557,7 +571,7 @@ void FreqWindow::DrawPlot()
hRuler->Refresh(false);
// Draw the plot
if (alg == 0)
if (alg == SpectrumAnalyst::Spectrum)
memDC.SetPen(wxPen(theTheme.Colour( clrHzPlot ), 1, wxSOLID));
else
memDC.SetPen(wxPen(theTheme.Colour( clrWavelengthPlot), 1, wxSOLID));
@ -568,9 +582,9 @@ void FreqWindow::DrawPlot()
float y;
if (mLogAxis)
y = GetProcessedValue(xPos, xPos * xStep);
y = mAnalyst->GetProcessedValue(xPos, xPos * xStep);
else
y = GetProcessedValue(xPos, xPos + xStep);
y = mAnalyst->GetProcessedValue(xPos, xPos + xStep);
float ynorm = (y - mYMin) / yTotal;
@ -725,13 +739,11 @@ float CubicMaximize(float y0, float y1, float y2, float y3, float * max)
}
}
float FreqWindow::GetProcessedValue(float freq0, float freq1)
float SpectrumAnalyst::GetProcessedValue(float freq0, float freq1) const
{
int alg = mAlgChoice->GetSelection();
float bin0, bin1, binwidth;
if (alg == 0) {
if (mAlg == Spectrum) {
bin0 = freq0 * mWindowSize / mRate;
bin1 = freq1 * mWindowSize / mRate;
} else {
@ -747,8 +759,8 @@ float FreqWindow::GetProcessedValue(float freq0, float freq1)
int ibin = int (binmid) - 1;
if (ibin < 1)
ibin = 1;
if (ibin >= mProcessedSize - 3)
ibin = mProcessedSize - 4;
if (ibin >= GetProcessedSize() - 3)
ibin = std::max(0, GetProcessedSize() - 4);
value = CubicInterpolate(mProcessed[ibin],
mProcessed[ibin + 1],
@ -756,6 +768,11 @@ float FreqWindow::GetProcessedValue(float freq0, float freq1)
mProcessed[ibin + 3], binmid - ibin);
} else {
if (bin0 < 0)
bin0 = 0;
if (bin1 >= GetProcessedSize())
bin1 = GetProcessedSize() - 1;
if (int (bin1) > int (bin0))
value += mProcessed[int (bin0)] * (int (bin0) + 1 - bin0);
bin0 = 1 + int (bin0);
@ -771,15 +788,63 @@ float FreqWindow::GetProcessedValue(float freq0, float freq1)
return value;
}
float SpectrumAnalyst::FindPeak(float xPos, float *pY) const
{
float bestpeak = 0.0f;
float bestValue = 0.0;
if (GetProcessedSize() > 1) {
bool up = (mProcessed[1] > mProcessed[0]);
float bestdist = 1000000;
for (int bin = 3; bin < GetProcessedSize() - 1; bin++) {
bool nowUp = mProcessed[bin] > mProcessed[bin - 1];
if (!nowUp && up) {
// Local maximum. Find actual value by cubic interpolation
int leftbin = bin - 2;
/*
if (leftbin < 1)
leftbin = 1;
*/
float valueAtMax = 0.0;
float max = leftbin + CubicMaximize(mProcessed[leftbin],
mProcessed[leftbin + 1],
mProcessed[leftbin + 2],
mProcessed[leftbin + 3],
&valueAtMax);
float thispeak;
if (mAlg == Spectrum)
thispeak = max * mRate / mWindowSize;
else
thispeak = max / mRate;
if (fabs(thispeak - xPos) < bestdist) {
bestpeak = thispeak;
bestdist = fabs(thispeak - xPos);
bestValue = valueAtMax;
// Should this test come after the enclosing if?
if (thispeak > xPos)
break;
}
}
up = nowUp;
}
}
if (pY)
*pY = bestValue;
return bestpeak;
}
void FreqWindow::PlotPaint(wxPaintEvent & evt)
{
wxPaintDC dc( (wxWindow *) evt.GetEventObject() );
dc.DrawBitmap( *mBitmap, 0, 0, true );
if( mProcessed == NULL )
if( 0 == mAnalyst->GetProcessedSize() )
return;
int alg = mAlgChoice->GetSelection();
SpectrumAnalyst::Algorithm alg =
SpectrumAnalyst::Algorithm(mAlgChoice->GetSelection());
dc.SetFont(mFreqFont);
@ -789,7 +854,7 @@ void FreqWindow::PlotPaint(wxPaintEvent & evt)
float xMin, xMax, xRatio, xStep;
if (alg == 0) {
if (alg == SpectrumAnalyst::Spectrum) {
xMin = mRate / mWindowSize;
xMax = mRate / 2;
xRatio = xMax / xMin;
@ -799,52 +864,21 @@ void FreqWindow::PlotPaint(wxPaintEvent & evt)
xStep = (xMax - xMin) / width;
} else {
xMin = 0;
xMax = mProcessedSize / mRate;
xMax = mAnalyst->GetProcessedSize() / mRate;
xStep = (xMax - xMin) / width;
}
float xPos = xMin;
// Find the peak nearest the cursor and plot it
float bestpeak = float(0.0);
if ( r.Contains(mMouseX, mMouseY) & (mMouseX!=0) & (mMouseX!=r.width-1) ) {
if (mLogAxis)
xPos = xMin * pow(xStep, mMouseX - (r.x + 1));
else
xPos = xMin + xStep * (mMouseX - (r.x + 1));
bool up = (mProcessed[1] > mProcessed[0]);
float bestdist = 1000000;
float bestValue = 0.0;
for (int bin = 2; bin < mProcessedSize; bin++) {
bool nowUp = mProcessed[bin] > mProcessed[bin - 1];
if (!nowUp && up) {
// Local maximum. Find actual value by cubic interpolation
int leftbin = bin - 2;
if (leftbin < 1)
leftbin = 1;
float valueAtMax = 0.0;
float max = leftbin + CubicMaximize(mProcessed[leftbin],
mProcessed[leftbin + 1],
mProcessed[leftbin + 2],
mProcessed[leftbin + 3], &valueAtMax);
float thispeak;
if (alg == 0)
thispeak = max * mRate / mWindowSize;
else
thispeak = max / mRate;
if (fabs(thispeak - xPos) < bestdist) {
bestpeak = thispeak;
bestdist = fabs(thispeak - xPos);
bestValue = valueAtMax;
if (thispeak > xPos)
break;
}
}
up = nowUp;
}
float bestValue = 0;
float bestpeak = mAnalyst->FindPeak(xPos, &bestValue);
int px;
if (mLogAxis)
@ -861,10 +895,10 @@ void FreqWindow::PlotPaint(wxPaintEvent & evt)
if (mLogAxis) {
xPos = xMin * pow(xStep, mMouseX - (r.x + 1));
value = GetProcessedValue(xPos, xPos * xStep);
value = mAnalyst->GetProcessedValue(xPos, xPos * xStep);
} else {
xPos = xMin + xStep * (mMouseX - (r.x + 1));
value = GetProcessedValue(xPos, xPos + xStep);
value = mAnalyst->GetProcessedValue(xPos, xPos + xStep);
}
wxString info;
@ -873,7 +907,7 @@ void FreqWindow::PlotPaint(wxPaintEvent & evt)
const wxChar *xp;
const wxChar *pp;
if (alg == 0) {
if (alg == SpectrumAnalyst::Spectrum) {
xpitch = PitchName_Absolute(FreqToMIDInote(xPos));
peakpitch = PitchName_Absolute(FreqToMIDInote(bestpeak));
xp = xpitch.c_str();
@ -946,41 +980,80 @@ void FreqWindow::Plot()
void FreqWindow::Recalc()
{
//wxLogDebug(wxT("Starting FreqWindow::Recalc()"));
if (mProcessed)
delete[] mProcessed;
mProcessed = NULL;
if (!mData) {
mFreqPlot->Refresh(true);
return;
}
int alg = mAlgChoice->GetSelection();
SpectrumAnalyst::Algorithm alg =
SpectrumAnalyst::Algorithm(mAlgChoice->GetSelection());
int windowFunc = mFuncChoice->GetSelection();
long windowSize = 0;
(mSizeChoice->GetStringSelection()).ToLong(&windowSize);
mWindowSize = windowSize;
//Progress dialog over FFT operation
std::auto_ptr<ProgressDialog> progress
(new ProgressDialog(_("Plot Spectrum"),_("Drawing Spectrum")));
if(!mAnalyst->Calculate(alg, windowFunc, mWindowSize, mRate,
mData, mDataLen,
&mYMin, &mYMax, progress.get())) {
mFreqPlot->Refresh(true);
return;
}
if (alg == SpectrumAnalyst::Spectrum) {
if(mYMin < -dBRange)
mYMin = -dBRange;
if(mYMax <= -dBRange)
mYMax = -dBRange + 10.; // it's all out of range, but show a scale.
else
mYMax += .5;
}
//wxLogDebug(wxT("About to draw plot in FreqWindow::Recalc()"));
DrawPlot();
mFreqPlot->Refresh(true);
}
bool SpectrumAnalyst::Calculate(Algorithm alg, int windowFunc,
int windowSize, double rate,
const float *data, int dataLen,
float *pYMin, float *pYMax,
ProgressDialog *progress)
{
// Wipe old data
mProcessed.resize(0);
mRate = 0.0;
mWindowSize = 0;
// Validate inputs
int f = NumWindowFuncs();
if (!(windowSize >= 32 && windowSize <= 65536 &&
alg >= 0 && alg <= 4 && windowFunc >= 0 && windowFunc < f)) {
mFreqPlot->Refresh(true);
return;
alg >= SpectrumAnalyst::Spectrum &&
alg < SpectrumAnalyst::NumAlgorithms &&
windowFunc >= 0 && windowFunc < f)) {
return false;
}
if (dataLen < windowSize) {
return false;
}
// Now repopulate
mRate = rate;
mWindowSize = windowSize;
mAlg = alg;
if (mDataLen < mWindowSize) {
mFreqPlot->Refresh(true);
return;
}
mProcessed = new float[mWindowSize];
int half = mWindowSize / 2;
mProcessed.resize(mWindowSize);
int i;
for (i = 0; i < mWindowSize; i++)
mProcessed[i] = float(0.0);
int half = mWindowSize / 2;
float *in = new float[mWindowSize];
float *in2 = new float[mWindowSize];
@ -1002,26 +1075,23 @@ void FreqWindow::Recalc()
else
wss = 1.0;
//Progress dialog over FFT operation
ProgressDialog *mProgress = new ProgressDialog(_("Plot Spectrum"),_("Drawing Spectrum"));
int start = 0;
int windows = 0;
while (start + mWindowSize <= mDataLen) {
while (start + mWindowSize <= dataLen) {
for (i = 0; i < mWindowSize; i++)
in[i] = win[i] * mData[start + i];
in[i] = win[i] * data[start + i];
switch (alg) {
case 0: // Spectrum
switch (alg) {
case Spectrum:
PowerSpectrum(mWindowSize, in, out);
for (i = 0; i < half; i++)
mProcessed[i] += out[i];
break;
case 1:
case 2:
case 3: // Autocorrelation, Cuberoot AC or Enhanced AC
case Autocorrelation:
case CubeRootAutocorrelation:
case EnhancedAutocorrelation:
// Take FFT
#ifdef EXPERIMENTAL_USE_REALFFTF
@ -1033,11 +1103,12 @@ void FreqWindow::Recalc()
for (i = 0; i < mWindowSize; i++)
in[i] = (out[i] * out[i]) + (out2[i] * out2[i]);
if (alg == 1) {
if (alg == Autocorrelation) {
for (i = 0; i < mWindowSize; i++)
in[i] = sqrt(in[i]);
}
if (alg == 2 || alg == 3) {
if (alg == CubeRootAutocorrelation ||
alg == EnhancedAutocorrelation) {
// Tolonen and Karjalainen recommend taking the cube root
// of the power, instead of the square root
@ -1056,7 +1127,7 @@ void FreqWindow::Recalc()
mProcessed[i] += out[i];
break;
case 4: // Cepstrum
case Cepstrum:
#ifdef EXPERIMENTAL_USE_REALFFTF
RealFFT(mWindowSize, in, out, out2);
#else
@ -1065,44 +1136,51 @@ void FreqWindow::Recalc()
// Compute log power
// Set a sane lower limit assuming maximum time amplitude of 1.0
float power;
float minpower = 1e-20*mWindowSize*mWindowSize;
for (i = 0; i < mWindowSize; i++)
{
power = (out[i] * out[i]) + (out2[i] * out2[i]);
if(power < minpower)
in[i] = log(minpower);
else
in[i] = log(power);
}
// Take IFFT
float power;
float minpower = 1e-20*mWindowSize*mWindowSize;
for (i = 0; i < mWindowSize; i++)
{
power = (out[i] * out[i]) + (out2[i] * out2[i]);
if(power < minpower)
in[i] = log(minpower);
else
in[i] = log(power);
}
// Take IFFT
#ifdef EXPERIMENTAL_USE_REALFFTF
InverseRealFFT(mWindowSize, in, NULL, out);
InverseRealFFT(mWindowSize, in, NULL, out);
#else
FFT(mWindowSize, true, in, NULL, out, out2);
FFT(mWindowSize, true, in, NULL, out, out2);
#endif
// Take real part of result
for (i = 0; i < half; i++)
mProcessed[i] += out[i];
// Take real part of result
for (i = 0; i < half; i++)
mProcessed[i] += out[i];
}
break;
default:
wxASSERT(false);
break;
} //switch
start += half;
windows++;
// only update the progress dialogue infrequently to reduce it's overhead
// only update the progress dialogue infrequently to reduce its overhead
// If we do it every time, it spends as much time updating X11 as doing
// the calculations. 10 seems a reasonable compromise on Linux that
// doesn't make it unresponsive, but avoids the slowdown.
if ((windows % 10) == 0)
mProgress->Update(1 - static_cast<float>(mDataLen - start) / mDataLen);
if (progress && (windows % 10) == 0)
progress->Update(1 - static_cast<float>(dataLen - start) / dataLen);
}
//wxLogDebug(wxT("Finished updating progress dialogue in FreqWindow::Recalc()"));
//wxLogDebug(wxT("Finished updating progress dialogue in SpectrumAnalyst::Recalc()"));
float mYMin = 1000000, mYMax = -1000000;
switch (alg) {
double scale;
case 0: // Spectrum
case Spectrum:
// Convert to decibels
mYMin = 1000000.;
mYMax = -1000000.;
@ -1115,19 +1193,10 @@ void FreqWindow::Recalc()
else if(mProcessed[i] < mYMin)
mYMin = mProcessed[i];
}
if(mYMin < -dBRange)
mYMin = -dBRange;
if(mYMax <= -dBRange)
mYMax = -dBRange + 10.; // it's all out of range, but show a scale.
else
mYMax += .5;
mProcessedSize = half;
mYStep = 10;
break;
case 1: // Standard Autocorrelation
case 2: // Cuberoot Autocorrelation
case Autocorrelation:
case CubeRootAutocorrelation:
for (i = 0; i < half; i++)
mProcessed[i] = mProcessed[i] / windows;
@ -1139,13 +1208,9 @@ void FreqWindow::Recalc()
mYMax = mProcessed[i];
else if (mProcessed[i] < mYMin)
mYMin = mProcessed[i];
mYStep = 1;
mProcessedSize = half;
break;
case 3: // Enhanced Autocorrelation
case EnhancedAutocorrelation:
for (i = 0; i < half; i++)
mProcessed[i] = mProcessed[i] / windows;
@ -1179,29 +1244,27 @@ void FreqWindow::Recalc()
mYMax = mProcessed[i];
else if (mProcessed[i] < mYMin)
mYMin = mProcessed[i];
mYStep = 1;
mProcessedSize = half;
break;
case 4: // Cepstrum
case Cepstrum:
for (i = 0; i < half; i++)
mProcessed[i] = mProcessed[i] / windows;
// Find min/max, ignoring first and last few values
int ignore = 4;
mYMin = mProcessed[ignore];
mYMax = mProcessed[ignore];
for (i = ignore + 1; i < half - ignore; i++)
if (mProcessed[i] > mYMax)
mYMax = mProcessed[i];
else if (mProcessed[i] < mYMin)
mYMin = mProcessed[i];
{
int ignore = 4;
mYMin = mProcessed[ignore];
mYMax = mProcessed[ignore];
for (i = ignore + 1; i < half - ignore; i++)
if (mProcessed[i] > mYMax)
mYMax = mProcessed[i];
else if (mProcessed[i] < mYMin)
mYMin = mProcessed[i];
}
break;
mYStep = 1;
mProcessedSize = half;
default:
wxASSERT(false);
break;
}
@ -1211,10 +1274,12 @@ void FreqWindow::Recalc()
delete[]out2;
delete[]win;
//wxLogDebug(wxT("About to draw plot in FreqWindow::Recalc()"));
DrawPlot();
mFreqPlot->Refresh(true);
delete mProgress;
if (pYMin)
*pYMin = mYMin;
if (pYMax)
*pYMax = mYMax;
return true;
}
void FreqWindow::OnExport(wxCommandEvent & WXUNUSED(event))
@ -1241,17 +1306,19 @@ void FreqWindow::OnExport(wxCommandEvent & WXUNUSED(event))
return;
}
const int processedSize = mAnalyst->GetProcessedSize();
const float *const processed = mAnalyst->GetProcessed();
if (mAlgChoice->GetSelection() == 0) {
f.AddLine(_("Frequency (Hz)\tLevel (dB)"));
for (int i = 1; i < mProcessedSize; i++)
for (int i = 1; i < processedSize; i++)
f.AddLine(wxString::
Format(wxT("%f\t%f"), i * mRate / mWindowSize,
mProcessed[i]));
processed[i]));
} else {
f.AddLine(_("Lag (seconds)\tFrequency (Hz)\tLevel"));
for (int i = 1; i < mProcessedSize; i++)
for (int i = 1; i < processedSize; i++)
f.AddLine(wxString::Format(wxT("%f\t%f\t%f"),
i / mRate, mRate / i, mProcessed[i]));
i / mRate, mRate / i, processed[i]));
}
#ifdef __WXMAC__

View File

@ -11,6 +11,8 @@
#ifndef __AUDACITY_FREQ_WINDOW__
#define __AUDACITY_FREQ_WINDOW__
#include <memory>
#include <vector>
#include <wx/brush.h>
#include <wx/frame.h>
#include <wx/panel.h>
@ -32,7 +34,7 @@ class FreqWindow;
class TrackList;
class FreqWindow;
class ProgressDialog;
class FreqPlot:public wxWindow {
public:
@ -50,6 +52,45 @@ class FreqPlot:public wxWindow {
DECLARE_EVENT_TABLE()
};
class SpectrumAnalyst
{
public:
enum Algorithm {
Spectrum,
Autocorrelation,
CubeRootAutocorrelation,
EnhancedAutocorrelation,
Cepstrum,
NumAlgorithms
};
SpectrumAnalyst();
~SpectrumAnalyst();
// Return true iff successful
bool Calculate(Algorithm alg,
int windowFunc, // see FFT.h for values
int windowSize, double rate,
const float *data, int dataLen,
float *pYMin = 0, float *pYMax = 0, // outputs
ProgressDialog *progress = 0);
const float *GetProcessed() const { return &mProcessed[0]; }
int GetProcessedSize() const { return mProcessed.size() / 2; }
float GetProcessedValue(float freq0, float freq1) const;
float FindPeak(float xPos, float *pY) const;
private:
Algorithm mAlg;
double mRate;
int mWindowSize;
std::vector<float> mProcessed;
};
class FreqWindow:public wxDialog {
public:
FreqWindow(wxWindow * parent, wxWindowID id,
@ -81,7 +122,7 @@ class FreqWindow:public wxDialog {
float *mBuffer;
bool mDrawGrid;
int mSize;
int mAlg;
SpectrumAnalyst::Algorithm mAlg;
int mFunc;
int mAxis;
int dBRange;
@ -126,8 +167,6 @@ class FreqWindow:public wxDialog {
int mDataLen;
float *mData;
int mWindowSize;
float *mProcessed;
int mProcessedSize;
bool mLogAxis;
float mYMin;
@ -139,7 +178,7 @@ class FreqWindow:public wxDialog {
int mMouseX;
int mMouseY;
float GetProcessedValue(float freq0, float freq1);
std::auto_ptr<SpectrumAnalyst> mAnalyst;
DECLARE_EVENT_TABLE()
};

View File

@ -48,6 +48,9 @@ simplifies construction of menu items.
#include <wx/statusbr.h>
#include <wx/utils.h>
#include "FreqWindow.h"
#include "TrackPanel.h"
#include "Project.h"
#include "effects/EffectManager.h"

View File

@ -92,6 +92,7 @@ scroll information. It also has some status flags.
#include "Project.h"
#include "FreqWindow.h"
#include "AutoRecovery.h"
#include "AudacityApp.h"
#include "AColor.h"

View File

@ -23,13 +23,12 @@
#include "DirManager.h"
#include "UndoManager.h"
#include "ViewInfo.h"
#include "TrackPanel.h"
#include "TrackPanelListener.h"
#include "AudioIO.h"
#include "commands/CommandManager.h"
#include "effects/EffectManager.h"
#include "xml/XMLTagHandler.h"
#include "toolbars/SelectionBar.h"
#include "FreqWindow.h"
#include "toolbars/SelectionBarListener.h"
#include <wx/defs.h>
#include <wx/event.h>
@ -56,13 +55,16 @@ class RecordingRecoveryHandler;
class TrackList;
class Tags;
class TrackPanel;
class FreqWindow;
// toolbar classes
class ControlToolBar;
class DeviceToolBar;
class EditToolBar;
class MeterToolBar;
class MixerToolBar;
class SelectionToolBar;
class SelectionBar;
class Toolbar;
class ToolManager;
class ToolsToolBar;
@ -365,7 +367,7 @@ class AUDACITY_DLL_API AudacityProject: public wxFrame,
LyricsWindow* GetLyricsWindow() { return mLyricsWindow; };
MixerBoard* GetMixerBoard() { return mMixerBoard; };
// SelectionBar callback methods
// SelectionBarListener callback methods
virtual double AS_GetRate();
virtual void AS_SetRate(double rate);

View File

@ -6,26 +6,52 @@
Dominic Mazzoni
*******************************************************************//**
\class SelectedRegion
\brief Defines a selected portion of a project
This includes starting and ending times, and other optional information
such as a frequency range, but not the set of selected tracks.
Maintains the invariants that ending time is not less than starting time
and that starting and ending frequencies, when both defined, are also
correctly ordered.
*//****************************************************************//**
**********************************************************************/
#ifndef __AUDACITY_SELECTEDREGION__
#define __AUDACITY_SELECTEDREGION__
#include "Audacity.h"
#include "Experimental.h"
class AUDACITY_DLL_API SelectedRegion {
// Maintains the invariant: t1() >= t0()
public:
static const int UndefinedFrequency = -1;
SelectedRegion()
: mT0(0.0)
, mT1(0.0)
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
, mF0(UndefinedFrequency)
, mF1(UndefinedFrequency)
#endif
{}
SelectedRegion(double t0, double t1)
: mT0(t0)
, mT1(t1)
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
, mF0(UndefinedFrequency)
, mF1(UndefinedFrequency)
#endif
{ ensureOrdering(); }
@ -37,6 +63,10 @@ public:
SelectedRegion(const SelectedRegion &x)
: mT0(x.mT0)
, mT1(x.mT1)
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
, mF0(x.mF0)
, mF1(x.mF1)
#endif
{}
SelectedRegion& operator=(const SelectedRegion& x)
@ -44,15 +74,34 @@ public:
if (this != &x) {
mT0 = x.mT0;
mT1 = x.mT1;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
mF0 = x.mF0;
mF1 = x.mF1;
#endif
}
return *this;
}
// Accessors
double t0() const { return mT0; }
double t1() const { return mT1; }
double duration() const { return mT1 - mT0; }
bool isPoint() const { return mT1 <= mT0; }
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
double f0() const { return mF0; }
double f1() const { return mF1; }
double fc() const {
if (mF0 == UndefinedFrequency ||
mF1 == UndefinedFrequency)
return UndefinedFrequency;
else
return sqrt(mF0 * mF1);
};
#endif
// Mutators
// PRL: to do: more integrity checks
// Returns true iff the bounds got swapped
@ -105,6 +154,28 @@ public:
void collapseToT1() { mT0 = mT1; }
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// Returns true iff the bounds got swapped
bool setF0(double f) {
mF0 = f;
return ensureFrequencyOrdering();
}
// Returns true iff the bounds got swapped
bool setF1(double f) {
mF1 = f;
return ensureFrequencyOrdering();
}
// Returns true iff the bounds got swapped
bool setFrequencies(double f0, double f1)
{
mF0 = f0;
mF1 = f1;
return ensureFrequencyOrdering();
}
#endif
private:
bool ensureOrdering()
{
@ -118,8 +189,33 @@ private:
return false;
}
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
bool ensureFrequencyOrdering()
{
if (mF1 < 0)
mF1 = UndefinedFrequency;
if (mF0 < 0)
mF0 = UndefinedFrequency;
if (mF0 != UndefinedFrequency &&
mF1 != UndefinedFrequency &&
mF1 < mF0) {
const double t = mF1;
mF1 = mF0;
mF0 = t;
return true;
}
else
return false;
}
#endif
double mT0;
double mT1;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
double mF0; // low frequency
double mF1; // high frequency
#endif
};

View File

@ -1780,6 +1780,8 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
bool autocorrelation,
bool logF)
{
enum { MONOCHROME_LINE = 230, COLORED_LINE = 0 };
#if PROFILE_WAVEFORM
# ifdef __WXMSW__
__time64_t tv0, tv1;
@ -1794,6 +1796,13 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
double sel0 = viewInfo->selectedRegion.t0();
double sel1 = viewInfo->selectedRegion.t1();
double freqLo = SelectedRegion::UndefinedFrequency;
double freqHi = SelectedRegion::UndefinedFrequency;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
freqLo = viewInfo->selectedRegion.f0();
freqHi = viewInfo->selectedRegion.f1();
#endif
double tOffset = clip->GetOffset();
double rate = clip->GetRate();
double sps = 1./rate;
@ -1922,7 +1931,7 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
minFreq = GetSpectrumLogMinFreq(ifreq/1000.0);
if(minFreq < 1)
// Paul L: I suspect this line is now unreachable
minFreq = ifreq/1000.0;
minFreq = 1.0;
}
bool usePxCache = false;
@ -1953,9 +1962,15 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
#endif
}
// PRL: Must the following two be integers?
int minSamples = int ((double)minFreq * (double)windowSize / rate + 0.5); // units are fft bins
int maxSamples = int ((double)maxFreq * (double)windowSize / rate + 0.5);
float binPerPx = float(maxSamples - minSamples) / float(mid.height);
float selBinLo = freqLo * (double)windowSize / rate;
float selBinHi = freqHi * (double)windowSize / rate;
float selBinCenter =
((freqLo < 0 || freqHi < 0) ? -1 : sqrt(freqLo * freqHi))
* (double)windowSize / rate;
int x = 0;
sampleCount w1 = (sampleCount) ((t0*rate + x *rate *tstep) + .5);
@ -2024,15 +2039,31 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
if (!logF)
{
for (int yy = 0; yy < mid.height; yy++) {
bool selflag = (ssel0 <= w0 && w1 < ssel1);
float bin0 = float (yy) * binPerPx + minSamples;
float bin1 = float (yy + 1) * binPerPx + minSamples;
bool centerLine = false;
AColor::ColorGradientChoice selected =
AColor::ColorGradientUnselected;
if (ssel0 <= w0 && w1 < ssel1)
{
if (selBinCenter >= 0 &&
bin0 <= selBinCenter &&
selBinCenter < bin1)
centerLine = true;
else if((selBinLo < 0 || selBinLo < bin1) &&
(selBinHi < 0 || selBinHi > bin0))
selected =
AColor::ColorGradientTimeAndFrequencySelected;
else
selected =
AColor::ColorGradientTimeSelected;
}
unsigned char rv, gv, bv;
float value;
if(!usePxCache) {
float bin0 = float (yy) * binPerPx + minSamples;
float bin1 = float (yy + 1) * binPerPx + minSamples;
if (int (bin1) == int (bin0))
value = freq[half * x + int (bin0)];
else {
@ -2069,7 +2100,11 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
else
value = clip->mSpecPxCache->values[x * mid.height + yy];
GetColorGradient(value, selflag, mIsGrayscale, &rv, &gv, &bv);
if(centerLine)
// Draw center frequency line
rv = gv = bv = (mIsGrayscale ? MONOCHROME_LINE : COLORED_LINE);
else
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv);
int px = ((mid.height - 1 - yy) * mid.width + x) * 3;
data[px++] = rv;
@ -2079,7 +2114,6 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
}
else //logF
{
bool selflag = (ssel0 <= w0 && w1 < ssel1);
unsigned char rv, gv, bv;
float value;
int x0=x*half;
@ -2151,19 +2185,38 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
float yy2 = yy2_base;
double exp_scale_per_height = exp(scale/mid.height);
for (int yy = 0; yy < mid.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);
bool centerLine = false;
AColor::ColorGradientChoice selected =
AColor::ColorGradientUnselected;
if (ssel0 <= w0 && w1 < ssel1)
{
if (selBinCenter >= 0 &&
bin0 <= selBinCenter &&
selBinCenter < bin1)
centerLine = true;
else if((selBinLo < 0 || selBinLo < bin1) &&
(selBinHi < 0 || selBinHi > bin0))
selected =
AColor::ColorGradientTimeAndFrequencySelected;
else
selected =
AColor::ColorGradientTimeSelected;
}
if(!usePxCache) {
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);
#ifdef EXPERIMENTAL_FIND_NOTES
if (mFftFindNotes) {
@ -2204,12 +2257,16 @@ void TrackArtist::DrawClipSpectrum(WaveTrack *track,
if (value < 0.0)
value = float(0.0);
clip->mSpecPxCache->values[x * mid.height + yy] = value;
yy2 = yy2_base;
}
else
value = clip->mSpecPxCache->values[x * mid.height + yy];
yy2 = yy2_base;
GetColorGradient(value, selflag, mIsGrayscale, &rv, &gv, &bv);
if(centerLine)
// Draw center frequency line
rv = gv = bv = (mIsGrayscale ? MONOCHROME_LINE : COLORED_LINE);
else
GetColorGradient(value, selected, mIsGrayscale, &rv, &gv, &bv);
#ifdef EXPERIMENTAL_FFT_Y_GRID
if (mFftYGrid && yGrid[yy]) {

View File

@ -184,6 +184,8 @@ is time to refresh some aspect of the screen.
#include <wx/intl.h>
#include <wx/image.h>
#include "FreqWindow.h" // for SpectrumAnalyst
#include "AColor.h"
#include "AllThemeResources.h"
#include "AudacityApp.h"
@ -459,6 +461,7 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
mTrackArtist(NULL),
mBacking(NULL),
mRefreshBacking(false),
mConverter(),
mAutoScrolling(false),
mVertScrollRemainder(0),
vrulerSize(36,0)
@ -561,6 +564,11 @@ TrackPanel::TrackPanel(wxWindow * parent, wxWindowID id,
wxCommandEventHandler(TrackPanel::OnTrackListUpdated),
NULL,
this);
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
mFreqSelMode = FREQ_SEL_INVALID;
mFrequencySnapper.reset(new SpectrumAnalyst());
#endif
}
TrackPanel::~TrackPanel()
@ -1509,6 +1517,7 @@ bool TrackPanel::SetCursorByActivity( )
#endif
case IsOverCutLine:
SetCursor( unsafe ? *mDisabledCursor : *mArrowCursor);
// what, no return true here? -- PRL
default:
break;
}
@ -1595,6 +1604,11 @@ void TrackPanel::SetCursorAndTipWhenInLabelTrack( LabelTrack * pLT,
}
}
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// Seems 4 is too small to work at the top. Why?
enum { FREQ_SNAP_DISTANCE = 10 };
#endif
// The select tool can have different cursors and prompts depending on what
// we hover over, most notably when hovering over the selction boundaries.
// Determine and set the cursor and tip accordingly.
@ -1678,6 +1692,97 @@ void TrackPanel::SetCursorAndTipWhenSelectTool( Track * t,
*ppTip = _("Click and drag to move right selection boundary.");
SetCursor(*mAdjustRightSelectionCursor);
}
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
else if (mAdjustSelectionEdges &&
!mViewInfo->selectedRegion.isPoint() &&
t->GetKind() == Track::Wave)
{
const WaveTrack *wt = static_cast<WaveTrack*>(t);
const int display = wt->GetDisplay();
const bool logF = display == WaveTrack::SpectrumLogDisplay;
if(logF || display == WaveTrack::SpectrumDisplay) {
double centerFrequency = mViewInfo->selectedRegion.fc();
if (event.AltDown()) {
if (event.ShiftDown()) {
// Even if top or bottom is not defined, it un-snaps
*ppTip = _("Click and drag to adjust boundaries of selected frequency band.");
return; // default cursor
}
else if (mFreqSelMode == FREQ_SEL_SNAPPING_CENTER) {
*ppTip = _("Move to snap center to peak frequencies, then click and drag to adjust band width.");
return; // default cursor
}
else if (centerFrequency >= 0) {
// PRL: Todo: fix message for Mac, say "Command" ?
*ppTip =
_("Click and drag to adjust band width about a fixed center, or hit Control to move the center.");
return; // default cursor
}
}
else {
// Is the cursor over the geometric mean frequency?
bool adjustCenter = false;
if (centerFrequency >= 0) {
const wxInt64 centerSel =
FrequencyToPosition(centerFrequency, r.y, r.height,
wt->GetRate(), logF);
adjustCenter =
within(event.m_y, centerSel, SELECTION_RESIZE_REGION);
}
if (adjustCenter) {
*ppTip =
_("Click and drag to adjust center selection frequency.");
SetCursor(*mEnvelopeCursor); // For want of a better...
return;
}
// Is the cursor over the bottom selection boundary?
bool adjust_bottom = false;
if (mViewInfo->selectedRegion.f0() < 0)
// Should we un-snap the bottom from "all"?
adjust_bottom =
within(event.m_y, r.y + r.height, FREQ_SNAP_DISTANCE);
else {
const wxInt64 bottomSel =
FrequencyToPosition(mViewInfo->selectedRegion.f0(),
r.y, r.height,
wt->GetRate(), logF);
adjust_bottom =
within(event.m_y, bottomSel, SELECTION_RESIZE_REGION);
}
if (adjust_bottom) {
*ppTip =
_("Click and drag to adjust bottom selection frequency.");
SetCursor(*mEnvelopeCursor); // For want of a better...
return;
}
// Is the cursor over the top selection boundary?
bool adjust_top = false;
if (mViewInfo->selectedRegion.f1() < 0)
// Should we un-snap the top from "all"?
adjust_top =
within(event.m_y, r.y, FREQ_SNAP_DISTANCE);
else {
const wxInt64 topSel =
FrequencyToPosition(mViewInfo->selectedRegion.f1(),
r.y, r.height,
wt->GetRate(), logF);
adjust_top =
within(event.m_y, topSel, SELECTION_RESIZE_REGION);
}
if (adjust_top) {
*ppTip =
_("Click and drag to adjust top selection frequency.");
SetCursor(*mEnvelopeCursor); // For want of a better...
return;
}
}
}
}
#endif
#ifdef USE_MIDI
else if (HitTestStretch(t, r, event)) {
*ppTip = _("Click and drag to stretch within selected region.");
@ -1856,6 +1961,11 @@ void TrackPanel::HandleSelect(wxMouseEvent & event)
//Send the new selection state to the undo/redo stack:
MakeParentModifyState(false);
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// This stops center snapping with mouse movement
mFreqSelMode = FREQ_SEL_INVALID;
#endif
} else if (event.LeftDClick() && !event.ShiftDown()) {
if (!mCapturedTrack) {
wxRect r;
@ -1896,6 +2006,21 @@ void TrackPanel::HandleSelect(wxMouseEvent & event)
SetCapturedTrack( NULL );
MakeParentModifyState(false);
}
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
else if (!event.IsButton() && event.AltDown()) {
// Enter center-snapping mode,
// or readjust if there already
if ((mFreqSelMode == FREQ_SEL_SNAPPING_CENTER
// Already in this selection mode
||
mFreqSelCenter < 0
// No defined center
)
&&
!mViewInfo->selectedRegion.isPoint())
MoveSnappingFreqSelection(event.m_y, r.y, r.height, t);
}
#endif
done:
SelectionHandleDrag(event, t);
@ -1940,6 +2065,63 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
bool stretch = HitTestStretch(pTrack, r, event);
#endif
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
if (event.AltDown() && pTrack &&
pTrack->GetKind() == Track::Wave) {
const WaveTrack* wt = static_cast<WaveTrack*>(pTrack);
const int display = wt->GetDisplay();
const bool logF = display == WaveTrack::SpectrumLogDisplay;
if (logF || display == WaveTrack::SpectrumDisplay) {
mFreqSelTrack = wt;
const double rate = wt->GetRate();
// If the Alt button is down, modify the current frequency selection.
// If not shift-alt, and center is defined, drag the width;
// otherwise edit the selection boundary nearest the mouse click.
bool dragWidth = false;
mFreqSelCenter = mViewInfo->selectedRegion.fc();
if (!event.ShiftDown() && mFreqSelCenter >= 0)
dragWidth = true;
double selend =
PositionToFrequency(false, event.m_y, r.y, r.height, rate, logF);
double high =
mViewInfo->selectedRegion.f1() < 0
? rate / 2 : mViewInfo->selectedRegion.f1();
double low =
mViewInfo->selectedRegion.f0() < 0
? 0 : mViewInfo->selectedRegion.f0();
if (logF)
selend = log(std::max(1.0, selend)),
high = log(std::max(1.0, high)),
low = log(std::max(1.0, low));
const bool adjustLow = (fabs (selend - low) < fabs (selend - high));
if (adjustLow) {
mFreqSelMode = FREQ_SEL_BOTTOM_FREE;
mFreqSelStart = mViewInfo->selectedRegion.f1();
}
else {
mFreqSelMode = FREQ_SEL_TOP_FREE;
mFreqSelStart = mViewInfo->selectedRegion.f0();
}
// Do not adjust time boundaries
mSelStart = -1;
ExtendFreqSelection(event.m_y, r.y, r.height, dragWidth);
UpdateSelectionDisplay();
// Frequency selection doesn't persist (yet?), so skip this:
// MakeParentModifyState(false);
return;
}
}
else
#endif
if (event.ShiftDown()
#ifdef USE_MIDI
&& !stretch
@ -1978,8 +2160,14 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
else
mSelStart = mViewInfo->selectedRegion.t0();
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// If drag starts, change time selection only
mFreqSelMode = FREQ_SEL_INVALID;
#endif
// If the shift button is down, extend the current selection.
ExtendSelection(event.m_x, r.x, pTrack);
UpdateSelectionDisplay();
MakeParentModifyState(false);
return;
}
@ -1992,10 +2180,10 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
&& !stretch
#endif
) {
AudacityProject *p = GetActiveProject();
if (p) {
AudacityProject *p = GetActiveProject();
if (p) {
double clicktime = PositionToTime(event.m_x, GetLeftOffset());
double clicktime = PositionToTime(event.m_x, GetLeftOffset());
const double t1 = mViewInfo->selectedRegion.t1();
double endtime = clicktime < t1 ? t1 : mViewInfo->total;
@ -2004,50 +2192,154 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
bool busy = gAudioIO->IsBusy();
if(!busy)
{
//If we aren't currently playing back, start playing back at
//the clicked point
ControlToolBar * ctb = p->GetControlToolBar();
//ctb->SetPlay(true);// Not needed as done in PlayPlayRegion
ctb->PlayPlayRegion(clicktime, endtime,false) ;
}
else
{
//If we are playing back, stop and move playback
//to the clicked point.
//This unpauses paused audio as well. The right thing to do might be to
//leave it paused but move the point. This would probably
//require a new method in ControlToolBar: SetPause();
ControlToolBar * ctb = p->GetControlToolBar();
ctb->StopPlaying();
ctb->PlayPlayRegion(clicktime,endtime,false) ;
}
{
//If we aren't currently playing back, start playing back at
//the clicked point
ControlToolBar * ctb = p->GetControlToolBar();
//ctb->SetPlay(true);// Not needed as done in PlayPlayRegion
ctb->PlayPlayRegion(clicktime, endtime,false) ;
}
else
{
//If we are playing back, stop and move playback
//to the clicked point.
//This unpauses paused audio as well. The right thing to do might be to
//leave it paused but move the point. This would probably
//require a new method in ControlToolBar: SetPause();
ControlToolBar * ctb = p->GetControlToolBar();
ctb->StopPlaying();
ctb->PlayPlayRegion(clicktime,endtime,false) ;
}
return;
}
return;
}
//Make sure you are within the selected track
if (pTrack && pTrack->GetSelected()) {
wxInt64 leftSel = TimeToPosition(mViewInfo->selectedRegion.t0(), r.x);
wxInt64 rightSel = TimeToPosition(mViewInfo->selectedRegion.t1(), r.x);
wxASSERT(leftSel <= rightSel);
// Adjusting selection edges can be turned off in the
// preferences now
if (!mAdjustSelectionEdges) {
}
// Is the cursor over the left selection boundary?
else if (within(event.m_x, leftSel, SELECTION_RESIZE_REGION)) {
// Pin the right selection boundary
if (mAdjustSelectionEdges) {
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// Check for dragging of top, bottom, or center
if (!mViewInfo->selectedRegion.isPoint() &&
pTrack->GetKind() == Track::Wave) {
const WaveTrack *wt = static_cast<WaveTrack*>(pTrack);
const int display = wt->GetDisplay();
const bool logF = display == WaveTrack::SpectrumLogDisplay;
if (logF || display == WaveTrack::SpectrumDisplay) {
// Adjust frequency selection, no modifier keys
// Is the cursor over the geometric mean frequency?
bool adjustCenter = false;
mFreqSelCenter = mViewInfo->selectedRegion.fc();
if (mFreqSelCenter >= 0) {
const wxInt64 centerSel =
FrequencyToPosition(mFreqSelCenter, r.y, r.height,
wt->GetRate(), logF);
adjustCenter =
within(event.m_y, centerSel, SELECTION_RESIZE_REGION);
}
if (adjustCenter) {
// Keep width constant
// Disable extension of time selection, unless also near
// left or right boundary as tested later
mSelStart = -1;
mFreqSelMode = FREQ_SEL_DRAG_CENTER;
mFreqSelTrack = wt;
// High (and low) must be defined if center is
// Use this to remember the starting ratio:
mFreqSelStart = mViewInfo->selectedRegion.f1() / mFreqSelCenter;
startNewSelection = false;
}
if (startNewSelection) {
// Is the cursor over the bottom selection boundary?
bool adjust_bottom = false;
if (mViewInfo->selectedRegion.f0() < 0)
// Should we un-snap the bottom from "all"?
adjust_bottom =
within(event.m_y, r.y + r.height, FREQ_SNAP_DISTANCE);
else {
const wxInt64 bottomSel =
FrequencyToPosition(mViewInfo->selectedRegion.f0(),
r.y, r.height,
wt->GetRate(), logF);
adjust_bottom =
within(event.m_y, bottomSel, SELECTION_RESIZE_REGION);
}
if (adjust_bottom) {
// Pin top edge
// Disable extension of time selection, unless also near
// left or right boundary as tested later
mSelStart = -1;
mFreqSelMode = FREQ_SEL_BOTTOM_FREE;
mFreqSelTrack = wt;
mFreqSelStart = mViewInfo->selectedRegion.f1();
startNewSelection = false;
}
}
if (startNewSelection) {
// Is the cursor over the bottom selection boundary?
bool adjust_top = false;
if (mViewInfo->selectedRegion.f1() < 0)
// Should we un-snap the top from "all"?
adjust_top =
within(event.m_y, r.y, FREQ_SNAP_DISTANCE);
else {
const wxInt64 topSel =
FrequencyToPosition(mViewInfo->selectedRegion.f1(),
r.y, r.height,
wt->GetRate(), logF);
adjust_top =
within(event.m_y, topSel, SELECTION_RESIZE_REGION);
}
if (adjust_top) {
// Pin bottom edge
// Disable extension of time selection, unless also near
// left or right boundary as tested later
mSelStart = -1;
mFreqSelMode = FREQ_SEL_TOP_FREE;
mFreqSelTrack = wt;
mFreqSelStart = mViewInfo->selectedRegion.f0();
startNewSelection = false;
}
}
} // spectrum type
} // wave track
#endif
wxInt64 leftSel = TimeToPosition(mViewInfo->selectedRegion.t0(), r.x);
wxInt64 rightSel = TimeToPosition(mViewInfo->selectedRegion.t1(), r.x);
wxASSERT(leftSel <= rightSel);
// Is the cursor over the left selection boundary?
if (within(event.m_x, leftSel, SELECTION_RESIZE_REGION)) {
// Pin the right selection boundary
mSelStart = mViewInfo->selectedRegion.t1();
startNewSelection = false;
}
// Is the cursor over the right selection boundary?
else if (within(event.m_x, rightSel, SELECTION_RESIZE_REGION)) {
// Pin the left selection boundary
if (startNewSelection) {
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// Enable time extension only
mFreqSelMode = FREQ_SEL_INVALID;
#endif
startNewSelection = false;
}
}
// Is the cursor over the right selection boundary?
else if (within(event.m_x, rightSel, SELECTION_RESIZE_REGION)) {
// Pin the left selection boundary
mSelStart = mViewInfo->selectedRegion.t0();
startNewSelection = false;
if (startNewSelection) {
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// Enable time extension only
mFreqSelMode = FREQ_SEL_INVALID;
#endif
startNewSelection = false;
}
}
}
}
@ -2160,6 +2452,9 @@ void TrackPanel::SelectionHandleClick(wxMouseEvent & event,
if (startNewSelection) {
// If we didn't move a selection boundary, start a new selection
SelectNone();
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
StartFreqSelection (event.m_y, r.y, r.height, pTrack);
#endif
StartSelection(event.m_x, r.x);
mTracks->Select(pTrack);
SetFocusedTrack(pTrack);
@ -2203,6 +2498,10 @@ void TrackPanel::StartSelection(int mouseXCoordinate, int trackLeftEdge)
void TrackPanel::ExtendSelection(int mouseXCoordinate, int trackLeftEdge,
Track *pTrack)
{
if (mSelStart < 0)
// Must be dragging frequency bounds only.
return;
double selend = PositionToTime(mouseXCoordinate, trackLeftEdge);
clip_bottom(selend, 0.0);
@ -2255,7 +2554,10 @@ void TrackPanel::ExtendSelection(int mouseXCoordinate, int trackLeftEdge,
//On-Demand: check to see if there is an OD thing associated with this track. If so we want to update the focal point for the task.
if (pTrack && (pTrack->GetKind() == Track::Wave) && ODManager::IsInstanceCreated())
ODManager::Instance()->DemandTrackUpdate((WaveTrack*)pTrack,sel0); //sel0 is sometimes less than mSelStart
}
void TrackPanel::UpdateSelectionDisplay()
{
// Full refresh since the label area may need to indicate
// newly selected tracks.
Refresh(false);
@ -2267,6 +2569,210 @@ void TrackPanel::ExtendSelection(int mouseXCoordinate, int trackLeftEdge,
DisplaySelection();
}
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
namespace {
inline double findMaxRatio(double center, double rate)
{
const double minFrequency = 1.0;
const double maxFrequency = (rate / 2.0);
const double frequency =
std::min(maxFrequency,
std::max(minFrequency, center));
return
std::min(frequency / minFrequency, maxFrequency / frequency);
}
}
void TrackPanel::StartSnappingFreqSelection (WaveTrack *pTrack)
{
static const sampleCount minLength = 8;
mFreqSelMode = FREQ_SEL_SNAPPING_CENTER;
const double rate = pTrack->GetRate();
// Grab samples, just for this track, at these times
std::vector<float> frequencySnappingData;
const sampleCount start =
pTrack->TimeToLongSamples(mViewInfo->selectedRegion.t0());
const sampleCount end =
pTrack->TimeToLongSamples(mViewInfo->selectedRegion.t1());
const sampleCount length =
std::min(sampleCount(frequencySnappingData.max_size()),
std::min(sampleCount(10485760), // as in FreqWindow.cpp
end - start));
const sampleCount effectiveLength = std::max(minLength, length);
frequencySnappingData.resize(effectiveLength, 0.0f);
pTrack->Get(
reinterpret_cast<samplePtr>(&frequencySnappingData[0]),
floatSample, start, length);
// Use same settings as are now used for spectrogram display,
// except, shrink the window as needed so we get some answers
int windowSize = mTrackArtist->GetSpectrumWindowSize();
while(windowSize > effectiveLength)
windowSize >>= 1;
int windowType;
gPrefs->Read(wxT("/Spectrum/WindowType"), &windowType, 3);
mFrequencySnapper->Calculate(
SpectrumAnalyst::Spectrum, windowType, windowSize, rate,
&frequencySnappingData[0], length);
// We can now throw away the sample data but we keep the spectrum.
}
void TrackPanel::MoveSnappingFreqSelection (int mouseYCoordinate,
int trackTopEdge,
int trackHeight, Track *pTrack)
{
if (pTrack &&
pTrack->GetSelected() &&
pTrack->GetKind() == Track::Wave)
{
WaveTrack* wt = static_cast<WaveTrack*>(pTrack);
const int display = wt->GetDisplay();
const bool logF = display == WaveTrack::SpectrumLogDisplay;
if (logF || display== WaveTrack::SpectrumDisplay)
{
const double rate = wt->GetRate();
if (mFreqSelMode != FREQ_SEL_SNAPPING_CENTER)
StartSnappingFreqSelection(wt);
const double frequency =
PositionToFrequency(false, mouseYCoordinate,
trackTopEdge, trackHeight, rate, logF);
const double snappedFrequency =
mFrequencySnapper->FindPeak(frequency, NULL);
const double maxRatio = findMaxRatio(snappedFrequency, rate);
double ratio = 2.0; // An arbitrary octave on each side, at most
if (mViewInfo->selectedRegion.f1() >= mViewInfo->selectedRegion.f0() &&
mViewInfo->selectedRegion.f0() >= 0)
// Preserve already chosen ratio instead
ratio = sqrt(mViewInfo->selectedRegion.f1() /
mViewInfo->selectedRegion.f0());
ratio = std::min(ratio, maxRatio);
// Set up what the alt-click code expects to find
mFreqSelCenter = snappedFrequency;
mViewInfo->selectedRegion.setFrequencies(
snappedFrequency / ratio, snappedFrequency * ratio);
mFreqSelTrack = wt;
// SelectNone();
// mTracks->Select(pTrack);
SetFocusedTrack(pTrack);
}
}
}
void TrackPanel::StartFreqSelection (int mouseYCoordinate, int trackTopEdge,
int trackHeight, Track *pTrack)
{
mFreqSelTrack = 0;
mFreqSelMode = FREQ_SEL_INVALID;
mFreqSelStart = mFreqSelCenter = SelectedRegion::UndefinedFrequency;
if (pTrack &&
pTrack->GetKind() == Track::Wave)
{
const WaveTrack* wt = static_cast<WaveTrack*>(pTrack);
const int display = wt->GetDisplay();
const bool logF = display == WaveTrack::SpectrumLogDisplay;
if (logF || display == WaveTrack::SpectrumDisplay)
{
mFreqSelTrack = wt;
mFreqSelMode = FREQ_SEL_FREE;
const double rate = wt->GetRate();
mFreqSelStart =
PositionToFrequency(false, mouseYCoordinate,
trackTopEdge, trackHeight, rate, logF);
}
}
mViewInfo->selectedRegion.setFrequencies(mFreqSelStart, mFreqSelStart);
}
void TrackPanel::ExtendFreqSelection(int mouseYCoordinate, int trackTopEdge,
int trackHeight, bool dragWidth)
{
// When dragWidth is true, and not dragging the center,
// adjust both top and bottom about geometric mean.
if (mFreqSelMode == FREQ_SEL_INVALID ||
mFreqSelMode == FREQ_SEL_SNAPPING_CENTER)
return;
// Extension happens only when dragging in the same track in which we
// started, and that is of a spectrogram display type.
const WaveTrack* wt = mFreqSelTrack;
const int display = wt->GetDisplay();
const bool logF = display == WaveTrack::SpectrumLogDisplay;
const double rate = wt->GetRate();
const double frequency =
PositionToFrequency(true, mouseYCoordinate,
trackTopEdge, trackHeight, rate, logF);
if (mFreqSelMode == FREQ_SEL_DRAG_CENTER) {
if (frequency == rate || frequency < 1.0)
// snapped to top or bottom
mViewInfo->selectedRegion.setFrequencies(
SelectedRegion::UndefinedFrequency,
SelectedRegion::UndefinedFrequency);
else {
// mFreqSelStart holds the ratio of top to center
const double maxRatio = findMaxRatio(frequency, rate);
const double ratio = std::min(maxRatio, mFreqSelStart);
mViewInfo->selectedRegion.setFrequencies(
frequency / ratio, frequency * ratio);
}
}
else if (dragWidth && mFreqSelCenter >= 0) {
if (frequency == rate || frequency < 1.0)
// snapped to top or bottom
mViewInfo->selectedRegion.setFrequencies(
SelectedRegion::UndefinedFrequency,
SelectedRegion::UndefinedFrequency);
else {
const double maxRatio = findMaxRatio(mFreqSelCenter, rate);
double ratio = frequency / mFreqSelCenter;
if (ratio < 1.0)
ratio = 1.0 / ratio;
ratio = std::min(maxRatio, ratio);
mViewInfo->selectedRegion.setFrequencies(
mFreqSelCenter / ratio, mFreqSelCenter * ratio);
}
}
else {
const bool bottomDefined =
!(mFreqSelMode == FREQ_SEL_TOP_FREE && mFreqSelStart < 0);
const bool topDefined =
!(mFreqSelMode == FREQ_SEL_BOTTOM_FREE && mFreqSelStart < 0);
if (!bottomDefined || (topDefined && mFreqSelStart < frequency)) {
// Adjust top
if (frequency == rate)
// snapped high; upper frequency is undefined
mViewInfo->selectedRegion.setF1(SelectedRegion::UndefinedFrequency);
else
mViewInfo->selectedRegion.setF1(std::max(1.0, frequency));
mViewInfo->selectedRegion.setF0(mFreqSelStart);
}
else {
// Adjust bottom
if (frequency < 1.0)
// snapped low; lower frequency is undefined
mViewInfo->selectedRegion.setF0(SelectedRegion::UndefinedFrequency);
else
mViewInfo->selectedRegion.setF0(std::min(rate / 2.0, frequency));
mViewInfo->selectedRegion.setF1(mFreqSelStart);
}
}
}
#endif
#ifdef USE_MIDI
void TrackPanel::Stretch(int mouseXCoordinate, int trackLeftEdge,
Track *pTrack)
@ -2404,34 +2910,39 @@ void TrackPanel::SelectionHandleDrag(wxMouseEvent & event, Track *clickedTrack)
// Can someone make this value of '5' configurable in
// preferences?
const int minimumSizedSelection = 5; //measured in pixels
wxInt64 SelStart=TimeToPosition( mSelStart, r.x); //cvt time to pixels.
// Abandon this drag if selecting < 5 pixels.
if (wxLongLong(SelStart-x).Abs() < minimumSizedSelection
// Might be dragging frequency bounds only, test
if (mSelStart >= 0) {
wxInt64 SelStart=TimeToPosition( mSelStart, r.x); //cvt time to pixels.
// Abandon this drag if selecting < 5 pixels.
if (wxLongLong(SelStart-x).Abs() < minimumSizedSelection
#ifdef USE_MIDI // limiting selection size is good, and not starting
&& !mStretching // stretch unless mouse moves 5 pixels is good, but
&& !mStretching // stretch unless mouse moves 5 pixels is good, but
#endif // once stretching starts, it's ok to move even 1 pixel
)
return;
)
return;
}
// Handle which tracks are selected
Track *sTrack = pTrack;
if (Track *eTrack = FindTrack(x, y, false, false, NULL)) {
// Swap the track pointers if needed
if (eTrack->GetIndex() < pTrack->GetIndex()) {
if (eTrack->GetIndex() < sTrack->GetIndex()) {
Track *t = eTrack;
eTrack = pTrack;
pTrack = t;
eTrack = sTrack;
sTrack = t;
}
TrackListIterator iter(mTracks);
pTrack = iter.StartWith(pTrack);
sTrack = iter.StartWith(sTrack);
do {
mTracks->Select(pTrack);
if (pTrack == eTrack) {
mTracks->Select(sTrack);
if (sTrack == eTrack) {
break;
}
pTrack = iter.Next();
} while (pTrack);
sTrack = iter.Next();
} while (sTrack);
}
#ifdef USE_MIDI
if (mStretching) {
@ -2443,7 +2954,15 @@ void TrackPanel::SelectionHandleDrag(wxMouseEvent & event, Track *clickedTrack)
return;
}
#endif
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
if (mFreqSelTrack == pTrack)
ExtendFreqSelection(y, r.y, r.height,
(event.AltDown() && !event.ShiftDown()));
#endif
ExtendSelection(x, r.x, clickedTrack);
UpdateSelectionDisplay();
}
/// Converts a position (mouse X coordinate) to
@ -2466,6 +2985,84 @@ wxInt64 TrackPanel::TimeToPosition(double projectTime,
trackLeftEdge);
}
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
/// Converts a position (mouse Y coordinate) to
/// frequency, in Hz.
double TrackPanel::PositionToFrequency(bool maySnap,
wxInt64 mouseYCoordinate,
wxInt64 trackTopEdge,
int trackHeight,
double rate,
bool logF) const
{
// Handle snapping
if (maySnap &&
mouseYCoordinate - trackTopEdge < FREQ_SNAP_DISTANCE)
return rate;
if (maySnap &&
trackTopEdge + trackHeight - mouseYCoordinate < FREQ_SNAP_DISTANCE)
return -1;
const double p = double(mouseYCoordinate - trackTopEdge) / trackHeight;
const int freq = lrint(rate/2.);
if (logF)
{
const double maxFreq =
std::min(freq, mTrackArtist->GetSpectrumLogMaxFreq(freq));
const double minFreq =
std::max(1, mTrackArtist->GetSpectrumLogMinFreq(1));
return exp(p * log(minFreq) + (1.0 - p) * log(maxFreq));
}
else
{
const double maxFreq =
std::min(freq, mTrackArtist->GetSpectrumMaxFreq(freq));
const double minFreq =
std::max(0, mTrackArtist->GetSpectrumMinFreq(0));
return p * minFreq + (1.0 - p) * maxFreq;
}
}
/// Converts a frequency to screen y position.
wxInt64 TrackPanel::FrequencyToPosition(double frequency,
wxInt64 trackTopEdge,
int trackHeight,
double rate,
bool logF) const
{
const int freq = lrint(rate/2.);
double p = 0;
if (logF)
{
const double maxFreq =
std::min(freq, mTrackArtist->GetSpectrumLogMaxFreq(freq));
const double minFreq =
std::max(1, mTrackArtist->GetSpectrumLogMinFreq(1));
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 double maxFreq =
std::min(freq, mTrackArtist->GetSpectrumMaxFreq(freq));
const double minFreq =
std::max(0, mTrackArtist->GetSpectrumMinFreq(0));
if (maxFreq > minFreq)
p = (frequency - minFreq) / (maxFreq - minFreq);
}
return trackTopEdge + wxInt64((1.0 - p) * trackHeight);
}
#endif
/// HandleEnvelope gets called when the user is changing the
/// amplitude envelope on a track.
void TrackPanel::HandleEnvelope(wxMouseEvent & event)
@ -5025,8 +5622,21 @@ void TrackPanel::OnCaptureKey(wxCommandEvent & event)
/// Allow typing into LabelTracks.
void TrackPanel::OnKeyDown(wxKeyEvent & event)
{
// Only deal with LabelTracks
Track *t = GetFocusedTrack();
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// Test very special conditions for freeing up the frequency selection
// for renewed center snapping
if (t &&
t->GetKind() == Track::Wave &&
event.GetKeyCode() == WXK_CONTROL &&
event.AltDown()) {
StartSnappingFreqSelection(static_cast<WaveTrack*>(t));
// And don't skip event, yet
}
#endif
// Only deal with LabelTracks
if (!t || t->GetKind() != Track::Label) {
event.Skip();
return;
@ -6836,8 +7446,8 @@ void TrackPanel::OnBoundaryMove(bool left, bool boundaryContract)
std::min(end,
mViewInfo->selectedRegion.t1() +
multiplier/mViewInfo->zoom));
}
}
}
Refresh( false );
MakeParentModifyState(false);
}

View File

@ -11,12 +11,15 @@
#ifndef __AUDACITY_TRACK_PANEL__
#define __AUDACITY_TRACK_PANEL__
#include <memory>
#include <wx/dcmemory.h>
#include <wx/dynarray.h>
#include <wx/panel.h>
#include <wx/timer.h>
#include <wx/window.h>
#include "Experimental.h"
#include "Sequence.h" //Stm: included for the sampleCount declaration
#include "WaveClip.h"
#include "WaveTrack.h"
@ -27,6 +30,7 @@ class wxMenu;
class wxRect;
class LabelTrack;
class SpectrumAnalyst;
class TrackPanel;
class TrackArtist;
class Ruler;
@ -54,31 +58,8 @@ class AUDACITY_DLL_API TrackClip
WX_DECLARE_OBJARRAY(TrackClip, TrackClipArray);
class AUDACITY_DLL_API TrackPanelListener {
public:
TrackPanelListener(){};
virtual ~TrackPanelListener(){};
virtual void TP_DisplaySelection() = 0;
virtual void TP_DisplayStatusMessage(wxString msg) = 0;
virtual int TP_GetCurrentTool() = 0;
virtual ToolsToolBar * TP_GetToolsToolBar() = 0;
virtual ControlToolBar * TP_GetControlToolBar() = 0;
virtual void TP_OnPlayKey() = 0;
virtual void TP_PushState(wxString shortDesc, wxString longDesc,
int flags = PUSH_AUTOSAVE | PUSH_CALC_SPACE) = 0;
virtual void TP_ModifyState(bool bWantsAutoSave) = 0; // if true, writes auto-save file. Should set only if you really want the state change restored after
// a crash, as it can take many seconds for large (eg. 10 track-hours) projects
virtual void TP_RedrawScrollbars() = 0;
virtual void TP_ScrollLeft() = 0;
virtual void TP_ScrollRight() = 0;
virtual void TP_ScrollWindow(double scrollto) = 0;
virtual void TP_ScrollUpDown(int delta) = 0;
virtual void TP_HandleResize() = 0;
};
// Declared elsewhere, to reduce compilation dependencies
class TrackPanelListener;
//
// TrackInfo sliders: we keep a pool of sliders, and attach them to tracks as
@ -334,6 +315,19 @@ class AUDACITY_DLL_API TrackPanel:public wxPanel {
virtual void StartSelection (int mouseXCoordinate, int trackLeftEdge);
virtual void ExtendSelection(int mouseXCoordinate, int trackLeftEdge,
Track *pTrack);
virtual void UpdateSelectionDisplay();
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
virtual void StartSnappingFreqSelection (WaveTrack *pTrack);
virtual void MoveSnappingFreqSelection (int mouseYCoordinate,
int trackTopEdge,
int trackHeight, Track *pTrack);
virtual void StartFreqSelection (int mouseYCoordinate, int trackTopEdge,
int trackHeight, Track *pTrack);
virtual void ExtendFreqSelection(int mouseYCoordinate, int trackTopEdge,
int trackHeight, bool dragWidth);
#endif
virtual void SelectTracksByLabel( LabelTrack *t );
virtual void SelectTrackLength(Track *t);
@ -563,6 +557,21 @@ protected:
double mSelStart;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
enum {
FREQ_SEL_INVALID,
FREQ_SEL_SNAPPING_CENTER,
FREQ_SEL_TOP_FREE,
FREQ_SEL_BOTTOM_FREE,
FREQ_SEL_DRAG_CENTER,
FREQ_SEL_FREE
} mFreqSelMode;
double mFreqSelStart;
double mFreqSelCenter; // Used when dragging the width about fixed center
const WaveTrack *mFreqSelTrack;
std::auto_ptr<SpectrumAnalyst> mFrequencySnapper;
#endif
Track *mCapturedTrack;
Envelope *mCapturedEnvelope;
WaveClip *mCapturedClip;
@ -628,6 +637,20 @@ protected:
wxInt64 TimeToPosition(double time,
wxInt64 trackLeftEdge) const;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
double PositionToFrequency(bool maySnap,
wxInt64 mouseYCoordinate,
wxInt64 trackTopEdge,
int trackHeight,
double rate,
bool logF) const;
wxInt64 FrequencyToPosition(double frequency,
wxInt64 trackTopEdge,
int trackHeight,
double rate,
bool logF) const;
#endif
int mInitialTrackHeight;
int mInitialUpperTrackHeight;
bool mAutoScrolling;
@ -734,6 +757,8 @@ protected:
//This constant determines the size of the horizontal region (in pixels) around
//the right and left selection bounds that can be used for horizontal selection adjusting
//(or, vertical distance around top and bottom bounds in spectrograms,
// for vertical selection adjusting)
#define SELECTION_RESIZE_REGION 3
#define SMOOTHING_KERNEL_RADIUS 3

View File

@ -401,7 +401,7 @@ class AUDACITY_DLL_API WaveTrack: public Track {
mLastDisplay=mDisplay; // remember last display mode for wave and wavedb so they can remap
mDisplay = display;
}
int GetDisplay() {return mDisplay;}
int GetDisplay() const {return mDisplay;}
int GetLastDisplay() {return mLastDisplay;}
void GetDisplayBounds(float *min, float *max);

View File

@ -17,6 +17,7 @@
*//*******************************************************************/
#include "GetProjectInfoCommand.h"
#include "../TrackPanel.h"
#include "../Project.h"
#include "../Track.h"
#include "../WaveTrack.h"

View File

@ -17,6 +17,7 @@
*//*******************************************************************/
#include "GetTrackInfoCommand.h"
#include "../TrackPanel.h"
#include "../Project.h"
#include "../Track.h"
#include "../WaveTrack.h"

View File

@ -26,6 +26,7 @@
#include <wx/valtext.h>
#include "../Audacity.h"
#include "../LabelTrack.h"
#include "../Envelope.h"
#include "../LabelTrack.h"
#include "../Prefs.h"

View File

@ -104,6 +104,10 @@ bool Effect::DoEffect(wxWindow *parent, int flags,
mTracks = list;
mT0 = t0;
mT1 = t1;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
mF0 = selectedRegion->f0();
mF1 = selectedRegion->f1();
#endif
CountWaveTracks();
// Note: Init may read parameters from preferences

View File

@ -21,6 +21,7 @@
class wxDialog;
class wxWindow;
#include "../Experimental.h"
#include "../WaveTrack.h"
#include "../Shuttle.h"
#include "../ShuttleGui.h"
@ -215,6 +216,10 @@ class AUDACITY_DLL_API Effect {
TrackList *mOutputTracks; // used only if CopyInputTracks() is called.
double mT0;
double mT1;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
double mF0;
double mF1;
#endif
TimeWarper *mWarper;
//

View File

@ -778,6 +778,24 @@ bool EffectNyquist::ProcessOne()
wxString cmd;
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
{
static const wxString varName(wxT("*F0*"));
if (mF0 < 0)
cmd += wxString::Format(wxT("(setf %s nil)\n"), varName);
else
cmd += wxString::Format(wxT("(setf %s (float %g))\n"), varName, mF0);
}
{
static const wxString varName(wxT("*F1*"));
if (mF1 < 0)
cmd += wxString::Format(wxT("(setf %s nil)\n"), varName);
else
cmd += wxString::Format(wxT("(setf %s (float %g))\n"), varName, mF1);
}
#endif
if (mDebug) {
cmd += wxT("(setf *tracenable* T)\n");
if (mExternal) {

View File

@ -18,6 +18,7 @@
#include <wx/combobox.h>
#include <wx/log.h>
#include <wx/process.h>
#include <wx/sizer.h>
#include <wx/textctrl.h>
#include <FileDialog.h>
#include "Export.h"

View File

@ -33,6 +33,7 @@
*//********************************************************************/
#include "../Audacity.h"
#include "../Experimental.h"
#include <wx/defs.h>
#include <wx/intl.h>
@ -105,6 +106,14 @@ void MousePrefs::CreateList()
AddItem(_("Left-Double-Click"), _("Select"), _("Select Clip or Entire Track"));
AddItem(_("Ctrl-Left-Click"), _("Select"), _("Set Selection Point and Play"));
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// Spectral selection
AddItem(_("Alt-Shift-Left-Click"), _("Select"), _("Adjust high or low frequency"));
AddItem(_("Alt-Left-Drag"), _("Select"), _("Adjust bandwidth"));
AddItem(_("Alt-Ctrl"), _("Select"), _("Unpin center frequency"));
AddItem(_("Alt-Move"), _("Select"), _("Snap center frequency to peaks"));
#endif
AddItem(_("Left-Click"), _("Zoom"), _("Zoom in on Point"));
AddItem(_("Left-Drag"), _("Zoom"), _("Zoom in on a Range"), _("same as right-drag"));
AddItem(_("Right-Click"), _("Zoom"), _("Zoom out one step"));
@ -131,6 +140,14 @@ void MousePrefs::CreateList()
AddItem(_("Right-Click"), _("Multi"), _("Zoom out one step"), _("same as zoom tool"));
AddItem(_("Right-Drag"), _("Multi"), _("Zoom in on a Range"), _("same as zoom tool"));
#ifdef EXPERIMENTAL_SPECTRAL_EDITING
// Spectral selection
AddItem(_("Alt-Shift-Left-Click"), _("Multi"), _("Adjust high or low frequency"), _("same as select tool"));
AddItem(_("Alt-Left-Drag"), _("Multi"), _("Adjust bandwidth"), _("same as select tool"));
AddItem(_("Alt-Ctrl"), _("Multi"), _("Unpin center frequency"), _("same as select tool"));
AddItem(_("Alt-Move"), _("Multi"), _("Snap center frequency to peaks"), _("same as select tool"));
#endif
AddItem(_("Wheel-Rotate"), _("Any"), _("Scroll up or down"));
AddItem(_("Shift-Wheel-Rotate"),_("Any"), _("Scroll left or right"));
AddItem(_("Ctrl-Wheel-Rotate"), _("Any"), _("Zoom in or out on Mouse Pointer"));

View File

@ -33,6 +33,7 @@
#include "DeviceToolBar.h"
#include "ToolDock.h"
#include "../TrackPanel.h"
#include "../AColor.h"
#include "../AllThemeResources.h"

View File

@ -43,6 +43,7 @@ with changes in the SelectionBar.
#endif
#include <wx/statline.h>
#include "SelectionBarListener.h"
#include "SelectionBar.h"
#include "../AudacityApp.h"

View File

@ -24,24 +24,9 @@ class wxDC;
class wxRadioButton;
class wxSizeEvent;
class SelectionBarListener;
class TimeTextCtrl;
class AUDACITY_DLL_API SelectionBarListener {
public:
SelectionBarListener(){};
virtual ~SelectionBarListener(){};
virtual double AS_GetRate() = 0;
virtual void AS_SetRate(double rate) = 0;
virtual int AS_GetSnapTo() = 0;
virtual void AS_SetSnapTo(int snap) = 0;
virtual const wxString & AS_GetSelectionFormat() = 0;
virtual void AS_SetSelectionFormat(const wxString & format) = 0;
virtual void AS_ModifySelection(double &start, double &end, bool done) = 0;
};
class SelectionBar:public ToolBar {
public:

View File

@ -27,6 +27,7 @@
#include <wx/intl.h>
#endif // WX_PRECOMP
#include "../Envelope.h"
#include "TranscriptionToolBar.h"
#include "ControlToolBar.h"