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:
parent
b84fdb82e1
commit
37608c2290
33
plug-ins/SpectralEditMulti.ny
Normal file
33
plug-ins/SpectralEditMulti.ny
Normal 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))
|
||||
|
32
plug-ins/SpectralEditParametricEQ.ny
Normal file
32
plug-ins/SpectralEditParametricEQ.ny
Normal 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))
|
36
plug-ins/SpectralEditShelves.ny
Normal file
36
plug-ins/SpectralEditShelves.ny
Normal 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))
|
||||
|
@ -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);
|
||||
|
13
src/AColor.h
13
src/AColor.h
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
@ -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()
|
||||
};
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
||||
};
|
||||
|
||||
|
@ -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]) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -17,6 +17,7 @@
|
||||
*//*******************************************************************/
|
||||
|
||||
#include "GetProjectInfoCommand.h"
|
||||
#include "../TrackPanel.h"
|
||||
#include "../Project.h"
|
||||
#include "../Track.h"
|
||||
#include "../WaveTrack.h"
|
||||
|
@ -17,6 +17,7 @@
|
||||
*//*******************************************************************/
|
||||
|
||||
#include "GetTrackInfoCommand.h"
|
||||
#include "../TrackPanel.h"
|
||||
#include "../Project.h"
|
||||
#include "../Track.h"
|
||||
#include "../WaveTrack.h"
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include <wx/valtext.h>
|
||||
|
||||
#include "../Audacity.h"
|
||||
#include "../LabelTrack.h"
|
||||
#include "../Envelope.h"
|
||||
#include "../LabelTrack.h"
|
||||
#include "../Prefs.h"
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
//
|
||||
|
@ -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) {
|
||||
|
@ -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"
|
||||
|
@ -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"));
|
||||
|
@ -33,6 +33,7 @@
|
||||
|
||||
#include "DeviceToolBar.h"
|
||||
#include "ToolDock.h"
|
||||
#include "../TrackPanel.h"
|
||||
|
||||
#include "../AColor.h"
|
||||
#include "../AllThemeResources.h"
|
||||
|
@ -43,6 +43,7 @@ with changes in the SelectionBar.
|
||||
#endif
|
||||
#include <wx/statline.h>
|
||||
|
||||
#include "SelectionBarListener.h"
|
||||
#include "SelectionBar.h"
|
||||
|
||||
#include "../AudacityApp.h"
|
||||
|
@ -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:
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <wx/intl.h>
|
||||
#endif // WX_PRECOMP
|
||||
|
||||
#include "../Envelope.h"
|
||||
#include "TranscriptionToolBar.h"
|
||||
|
||||
#include "ControlToolBar.h"
|
||||
|
Loading…
x
Reference in New Issue
Block a user