Audacity 3.2.0
SpectrumView.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5SpectrumView.cpp
6
7Paul Licameli split from WaveTrackView.cpp
8
9**********************************************************************/
10
11
12#include "SpectrumView.h"
13
14#include "SpectralDataManager.h" // Cycle :-(
15#include "SpectrumCache.h"
16
17#include "Sequence.h"
18#include "Spectrum.h"
19
21#include "WaveTrackView.h"
23
24#include "../../../ui/BrushHandle.h"
25
26#include "AColor.h"
27#include "Prefs.h"
28#include "NumberScale.h"
29#include "../../../../TrackArt.h"
30#include "../../../../TrackArtist.h"
31#include "../../../../TrackPanelDrawingContext.h"
32#include "ViewInfo.h"
33#include "WaveClip.h"
34#include "WaveTrack.h"
35#include "../../../../prefs/SpectrogramSettings.h"
36#include "../../../../ProjectSettings.h"
37#include "SampleTrackCache.h"
38#include "WaveTrackLocation.h"
39
40#include <wx/dcmemory.h>
41#include <wx/graphics.h>
42
43#include "float_cast.h"
44
45class BrushHandle;
46class SpectralData;
47
50 { wxT("Spectrogram"), XXO("&Spectrogram") }
51};
52
54
56 auto wt = static_cast<WaveTrack*>( FindTrack().get() );
57 mpSpectralData = std::make_shared<SpectralData>(wt->GetRate());
58 mOnBrushTool = false;
59}
60
62
64{
65 return true;
66}
67
69public:
71 : mView{ view }
72 {}
73
74 void Init( AudacityProject &project, bool clearAll ) override
75 {
76 mpProject = &project;
77 ForAll( project, [this, clearAll](SpectrumView &view){
78 auto pOldData = view.mpSpectralData;
79 if (clearAll) {
80 auto &pNewData = view.mpBackupSpectralData =
81 std::make_shared<SpectralData>(pOldData->GetSR());
82 pNewData->CopyFrom(*pOldData);
83 pOldData->clearAllData();
84 }
85 else {
86 // Back up one view only
87 if (&mView == &view) {
88 auto &pNewData = view.mpBackupSpectralData =
89 std::make_shared<SpectralData>(pOldData->GetSR());
90 pNewData->CopyFrom( *pOldData );
91 }
92 else
93 view.mpBackupSpectralData = {};
94 }
95 });
96 }
97
99 {
100 if (mpProject)
101 ForAll( *mpProject, [this](SpectrumView &view){
102 if (mCommitted) {
103 // Discard all backups
104 view.mpBackupSpectralData = {};
105 }
106 else {
107 // Restore all
108 if (auto &pBackupData = view.mpBackupSpectralData) {
109 view.mpSpectralData->CopyFrom(*pBackupData);
110 pBackupData.reset();
111 }
112 }
113 });
114 }
115
116private:
119};
120
121// This always hits, but details of the hit vary with mouse position and
122// key state.
124 std::weak_ptr<BrushHandle> &holder,
125 const TrackPanelMouseState &st, const AudacityProject *pProject,
126 const std::shared_ptr<SpectrumView> &pTrackView,
127 const std::shared_ptr<SpectralData> &mpData)
128{
129 const auto &viewInfo = ViewInfo::Get( *pProject );
130 auto &projectSettings = ProjectSettings::Get( *pProject );
131 auto result = std::make_shared<BrushHandle>(
132 std::make_shared<SpectrumView::SpectralDataSaver>(*pTrackView),
133 pTrackView, TrackList::Get( *pProject ),
134 st, viewInfo, mpData, projectSettings);
135
136 result = AssignUIHandlePtr(holder, result);
137
138 //Make sure we are within the selected track
139 // Adjusting the selection edges can be turned off in
140 // the preferences...
141 auto pTrack = pTrackView->FindTrack();
142 if (!pTrack->GetSelected() || !viewInfo.bAdjustSelectionEdges)
143 {
144 return result;
145 }
146
147 return result;
148}
149
151 std::function<void(SpectrumView &view)> fn )
152{
153 if (!fn)
154 return;
155 for ( const auto wt : TrackList::Get(project).Any< WaveTrack >() ) {
156 if (auto pWaveTrackView =
157 dynamic_cast<WaveTrackView*>( &TrackView::Get(*wt)) ) {
158 for (const auto &pSubView : pWaveTrackView->GetAllSubViews()) {
159 if (const auto sView = dynamic_cast<SpectrumView*>(pSubView.get()))
160 fn( *sView );
161 }
162 }
163 }
164}
165
166std::vector<UIHandlePtr> SpectrumView::DetailedHitTest(
167 const TrackPanelMouseState &state,
168 const AudacityProject *pProject, int currentTool, bool bMultiTool )
169{
170 const auto wt = std::static_pointer_cast< WaveTrack >( FindTrack() );
171 std::vector<UIHandlePtr> results;
172
173#ifdef EXPERIMENTAL_BRUSH_TOOL
174 mOnBrushTool = (currentTool == ToolCodes::brushTool);
175 if(mOnBrushTool){
176 const auto result = BrushHandleHitTest(
177 mBrushHandle, state,
178 pProject, std::static_pointer_cast<SpectrumView>(shared_from_this()),
180 results.push_back(result);
181 return results;
182 }
183#endif
184
186 state, pProject, currentTool, bMultiTool, wt
187 ).second;
188}
189
190void SpectrumView::DoSetMinimized( bool minimized )
191{
192 auto wt = static_cast<WaveTrack*>( FindTrack().get() );
193
194#ifdef EXPERIMENTAL_HALF_WAVE
195 bool bHalfWave;
196 gPrefs->Read(wxT("/GUI/CollapseToHalfWave"), &bHalfWave, false);
197 if( bHalfWave && minimized)
198 {
199 // It is all right to set the top of scale to a huge number,
200 // not knowing the track rate here -- because when retrieving the
201 // value, then we pass in a sample rate and clamp it above to the
202 // Nyquist frequency.
203 constexpr auto max = std::numeric_limits<float>::max();
204 const bool spectrumLinear =
207 // Zoom out full
209 .SetBounds( spectrumLinear ? 0.0f : 1.0f, max );
210 }
211#endif
212
213 TrackView::DoSetMinimized( minimized );
214}
215
216auto SpectrumView::SubViewType() const -> const Type &
217{
218 return sType;
219}
220
221std::shared_ptr<TrackVRulerControls> SpectrumView::DoGetVRulerControls()
222{
223 return std::make_shared<SpectrumVRulerControls>( shared_from_this() );
224}
225
226std::shared_ptr<SpectralData> SpectrumView::GetSpectralData(){
227 return mpSpectralData;
228}
229
231 if ( const auto pDest = dynamic_cast< SpectrumView* >( destSubView ) ) {
232 pDest->mpSpectralData = std::make_shared<SpectralData>(mpSpectralData->GetSR());
233 pDest->mpSpectralData->CopyFrom(*mpSpectralData);
234 }
235}
236
237namespace
238{
239
240static inline float findValue
241(const float *spectrum, float bin0, float bin1, unsigned nBins,
242 bool autocorrelation, int gain, int range)
243{
244 float value;
245
246
247#if 0
248 // Averaging method
249 if ((int)(bin1) == (int)(bin0)) {
250 value = spectrum[(int)(bin0)];
251 } else {
252 float binwidth= bin1 - bin0;
253 value = spectrum[(int)(bin0)] * (1.f - bin0 + (int)bin0);
254
255 bin0 = 1 + (int)(bin0);
256 while (bin0 < (int)(bin1)) {
257 value += spectrum[(int)(bin0)];
258 bin0 += 1.0;
259 }
260 // Do not reference past end of freq array.
261 if ((int)(bin1) >= (int)nBins) {
262 bin1 -= 1.0;
263 }
264
265 value += spectrum[(int)(bin1)] * (bin1 - (int)(bin1));
266 value /= binwidth;
267 }
268#else
269 // Maximum method, and no apportionment of any single bins over multiple pixel rows
270 // See Bug971
271 int index, limitIndex;
272 if (autocorrelation) {
273 // bin = 2 * nBins / (nBins - 1 - array_index);
274 // Solve for index
275 index = std::max(0.0f, std::min(float(nBins - 1),
276 (nBins - 1) - (2 * nBins) / (std::max(1.0f, bin0))
277 ));
278 limitIndex = std::max(0.0f, std::min(float(nBins - 1),
279 (nBins - 1) - (2 * nBins) / (std::max(1.0f, bin1))
280 ));
281 }
282 else {
283 index = std::min<int>(nBins - 1, (int)(floor(0.5 + bin0)));
284 limitIndex = std::min<int>(nBins, (int)(floor(0.5 + bin1)));
285 }
286 value = spectrum[index];
287 while (++index < limitIndex)
288 value = std::max(value, spectrum[index]);
289#endif
290 if (!autocorrelation) {
291 // Last step converts dB to a 0.0-1.0 range
292 value = (value + range + gain) / (double)range;
293 }
294 value = std::min(1.0f, std::max(0.0f, value));
295 return value;
296}
297
298// dashCount counts both dashes and the spaces between them.
300ChooseColorSet( float bin0, float bin1, float selBinLo,
301 float selBinCenter, float selBinHi, int dashCount, bool isSpectral )
302{
303 if (!isSpectral)
305 if ((selBinCenter >= 0) && (bin0 <= selBinCenter) &&
306 (selBinCenter < bin1))
308 if ((0 == dashCount % 2) &&
309 (((selBinLo >= 0) && (bin0 <= selBinLo) && ( selBinLo < bin1)) ||
310 ((selBinHi >= 0) && (bin0 <= selBinHi) && ( selBinHi < bin1))))
312 if ((selBinLo < 0 || selBinLo < bin1) && (selBinHi < 0 || selBinHi > bin0))
314
316}
317
319 SampleTrackCache &waveTrackCache,
320 const WaveClip *clip,
321 const wxRect &rect,
322 const std::shared_ptr<SpectralData> &mpSpectralData,
323 bool selected)
324{
325 auto &dc = context.dc;
326 const auto artist = TrackArtist::Get( context );
327 bool onBrushTool = artist->onBrushTool;
328 const auto &selectedRegion = *artist->pSelectedRegion;
329 const auto &zoomInfo = *artist->pZoomInfo;
330
331#ifdef PROFILE_WAVEFORM
332 Profiler profiler;
333#endif
334
335 //If clip is "too small" draw a placeholder instead of
336 //attempting to fit the contents into a few pixels
337 if (!WaveTrackView::ClipDetailsVisible(*clip, zoomInfo, rect))
338 {
339 auto clipRect = ClipParameters::GetClipRect(*clip, zoomInfo, rect);
340 TrackArt::DrawClipFolded(dc, clipRect);
341 return;
342 }
343
344 const auto track =
345 dynamic_cast<const WaveTrack*>(waveTrackCache.GetTrack().get());
346 if (!track)
347 // Leave a blank rectangle.
348 // TODO: rewrite GetSpectrumBounds so it is
349 // not a member of WaveTrack, but fetch UI related ClientData
350 // attachments; then this downcast from SampleTrack will not be needed.
351 return;
352
353 auto &settings = SpectrogramSettings::Get(*track);
354 const bool autocorrelation = (settings.algorithm == SpectrogramSettings::algPitchEAC);
355
356 enum { DASH_LENGTH = 10 /* pixels */ };
357
359 true, track, clip, rect, selectedRegion, zoomInfo };
360 const wxRect &hiddenMid = params.hiddenMid;
361 // The "hiddenMid" rect contains the part of the display actually
362 // containing the waveform, as it appears without the fisheye. If it's empty, we're done.
363 if (hiddenMid.width <= 0) {
364 return;
365 }
366
367 const double &t0 = params.t0;
368 const double &tOffset = params.tOffset;
369 const auto &ssel0 = params.ssel0;
370 const auto &ssel1 = params.ssel1;
371 const double &averagePixelsPerSample = params.averagePixelsPerSample;
372 const double &rate = params.rate;
373 const double &hiddenLeftOffset = params.hiddenLeftOffset;
374 const double &leftOffset = params.leftOffset;
375 const wxRect &mid = params.mid;
376
379#ifdef EXPERIMENTAL_SPECTRAL_EDITING
380 freqLo = selectedRegion.f0();
381 freqHi = selectedRegion.f1();
382#endif
383
384 const int &colorScheme = settings.colorScheme;
385 const int &range = settings.range;
386 const int &gain = settings.gain;
387
388#ifdef EXPERIMENTAL_FIND_NOTES
389 const bool &fftFindNotes = settings.fftFindNotes;
390 const double &findNotesMinA = settings.findNotesMinA;
391 const int &numberOfMaxima = settings.numberOfMaxima;
392 const bool &findNotesQuantize = settings.findNotesQuantize;
393#endif
394#ifdef EXPERIMENTAL_FFT_Y_GRID
395 const bool &fftYGrid = settings.fftYGrid;
396#endif
397
398 dc.SetPen(*wxTRANSPARENT_PEN);
399
400 // We draw directly to a bit image in memory,
401 // and then paint this directly to our offscreen
402 // bitmap. Note that this could be optimized even
403 // more, but for now this is not bad. -dmazzoni
404 wxImage image((int)mid.width, (int)mid.height);
405 if (!image.IsOk())
406 return;
407#ifdef EXPERIMENTAL_SPECTROGRAM_OVERLAY
408 image.SetAlpha();
409 unsigned char *alpha = image.GetAlpha();
410#endif
411 unsigned char *data = image.GetData();
412
413 const auto half = settings.GetFFTLength() / 2;
414 const double binUnit = rate / (2 * half);
415 const float *freq = 0;
416 const sampleCount *where = 0;
417 bool updated;
418 {
419 const double pps = averagePixelsPerSample * rate;
420 updated = WaveClipSpectrumCache::Get( *clip ).GetSpectrogram( *clip,
421 waveTrackCache, freq, where,
422 (size_t)hiddenMid.width,
423 t0, pps);
424 }
425 auto nBins = settings.NBins();
426
427 float minFreq, maxFreq;
429 .GetBounds(*track, minFreq, maxFreq);
430
431 const SpectrogramSettings::ScaleType scaleType = settings.scaleType;
432
433 // nearest frequency to each pixel row from number scale, for selecting
434 // the desired fft bin(s) for display on that row
435 float *bins = (float*)alloca(sizeof(*bins)*(hiddenMid.height + 1));
436 {
437 const NumberScale numberScale( settings.GetScale( minFreq, maxFreq ) );
438
439 NumberScale::Iterator it = numberScale.begin(mid.height);
440 float nextBin = std::max( 0.0f, std::min( float(nBins - 1),
441 settings.findBin( *it, binUnit ) ) );
442
443 int yy;
444 for (yy = 0; yy < hiddenMid.height; ++yy) {
445 bins[yy] = nextBin;
446 nextBin = std::max( 0.0f, std::min( float(nBins - 1),
447 settings.findBin( *++it, binUnit ) ) );
448 }
449 bins[yy] = nextBin;
450 }
451
452#ifdef EXPERIMENTAL_FFT_Y_GRID
453 const float
454 log2 = logf(2.0f),
455 scale2 = (lmax - lmin) / log2,
456 lmin2 = lmin / log2;
457
458 ArrayOf<bool> yGrid{size_t(mid.height)};
459 for (int yy = 0; yy < mid.height; ++yy) {
460 float n = (float(yy) / mid.height*scale2 - lmin2) * 12;
461 float n2 = (float(yy + 1) / mid.height*scale2 - lmin2) * 12;
462 float f = float(minFreq) / (fftSkipPoints + 1)*powf(2.0f, n / 12.0f + lmin2);
463 float f2 = float(minFreq) / (fftSkipPoints + 1)*powf(2.0f, n2 / 12.0f + lmin2);
464 n = logf(f / 440) / log2 * 12;
465 n2 = logf(f2 / 440) / log2 * 12;
466 if (floor(n) < floor(n2))
467 yGrid[yy] = true;
468 else
469 yGrid[yy] = false;
470 }
471#endif //EXPERIMENTAL_FFT_Y_GRID
472
473 auto &clipCache = WaveClipSpectrumCache::Get( *clip );
474 if (!updated && clipCache.mSpecPxCache->valid &&
475 ((int)clipCache.mSpecPxCache->len == hiddenMid.height * hiddenMid.width)
476 && scaleType == clipCache.mSpecPxCache->scaleType
477 && gain == clipCache.mSpecPxCache->gain
478 && range == clipCache.mSpecPxCache->range
479 && minFreq == clipCache.mSpecPxCache->minFreq
480 && maxFreq == clipCache.mSpecPxCache->maxFreq
481#ifdef EXPERIMENTAL_FFT_Y_GRID
482 && fftYGrid==fftYGridOld
483#endif //EXPERIMENTAL_FFT_Y_GRID
484#ifdef EXPERIMENTAL_FIND_NOTES
485 && fftFindNotes == artist->fftFindNotesOld
486 && findNotesMinA == artist->findNotesMinAOld
487 && numberOfMaxima == artist->findNotesNOld
488 && findNotesQuantize == artist->findNotesQuantizeOld
489#endif
490 ) {
491 // Wave clip's spectrum cache is up to date,
492 // and so is the spectrum pixel cache
493 }
494 else {
495 // Update the spectrum pixel cache
496 clipCache.mSpecPxCache = std::make_unique<SpecPxCache>(hiddenMid.width * hiddenMid.height);
497 clipCache.mSpecPxCache->valid = true;
498 clipCache.mSpecPxCache->scaleType = scaleType;
499 clipCache.mSpecPxCache->gain = gain;
500 clipCache.mSpecPxCache->range = range;
501 clipCache.mSpecPxCache->minFreq = minFreq;
502 clipCache.mSpecPxCache->maxFreq = maxFreq;
503#ifdef EXPERIMENTAL_FIND_NOTES
504 artist->fftFindNotesOld = fftFindNotes;
505 artist->findNotesMinAOld = findNotesMinA;
506 artist->findNotesNOld = numberOfMaxima;
507 artist->findNotesQuantizeOld = findNotesQuantize;
508#endif
509
510#ifdef EXPERIMENTAL_FIND_NOTES
511 float log2 = logf( 2.0f ),
512 lmin = logf( minFreq ), lmax = logf( maxFreq ), scale = lmax - lmin,
513 lmins = lmin,
514 lmaxs = lmax
515 ;
516#endif //EXPERIMENTAL_FIND_NOTES
517
518#ifdef EXPERIMENTAL_FIND_NOTES
519 int maxima[128];
520 float maxima0[128], maxima1[128];
521 const float
522 f2bin = half / (rate / 2.0f),
523 bin2f = 1.0f / f2bin,
524 minDistance = powf(2.0f, 2.0f / 12.0f),
525 i0 = expf(lmin) / binUnit,
526 i1 = expf(scale + lmin) / binUnit,
527 minColor = 0.0f;
528 const size_t maxTableSize = 1024;
529 ArrayOf<int> indexes{ maxTableSize };
530#endif //EXPERIMENTAL_FIND_NOTES
531
532#ifdef _OPENMP
533#pragma omp parallel for
534#endif
535 for (int xx = 0; xx < hiddenMid.width; ++xx) {
536#ifdef EXPERIMENTAL_FIND_NOTES
537 int maximas = 0;
538 const int x0 = nBins * xx;
539 if (fftFindNotes) {
540 for (int i = maxTableSize - 1; i >= 0; i--)
541 indexes[i] = -1;
542
543 // Build a table of (most) values, put the index in it.
544 for (int i = (int)(i0); i < (int)(i1); i++) {
545 float freqi = freq[x0 + (int)(i)];
546 int value = (int)((freqi + gain + range) / range*(maxTableSize - 1));
547 if (value < 0)
548 value = 0;
549 if (value >= maxTableSize)
550 value = maxTableSize - 1;
551 indexes[value] = i;
552 }
553 // Build from the indices an array of maxima.
554 for (int i = maxTableSize - 1; i >= 0; i--) {
555 int index = indexes[i];
556 if (index >= 0) {
557 float freqi = freq[x0 + index];
558 if (freqi < findNotesMinA)
559 break;
560
561 bool ok = true;
562 for (int m = 0; m < maximas; m++) {
563 // Avoid to store very close maxima.
564 float maxm = maxima[m];
565 if (maxm / index < minDistance && index / maxm < minDistance) {
566 ok = false;
567 break;
568 }
569 }
570 if (ok) {
571 maxima[maximas++] = index;
572 if (maximas >= numberOfMaxima)
573 break;
574 }
575 }
576 }
577
578// The f2pix helper macro converts a frequency into a pixel coordinate.
579#define f2pix(f) (logf(f)-lmins)/(lmaxs-lmins)*hiddenMid.height
580
581 // Possibly quantize the maxima frequencies and create the pixel block limits.
582 for (int i = 0; i < maximas; i++) {
583 int index = maxima[i];
584 float f = float(index)*bin2f;
585 if (findNotesQuantize)
586 {
587 f = expf((int)(log(f / 440) / log2 * 12 - 0.5) / 12.0f*log2) * 440;
588 maxima[i] = f*f2bin;
589 }
590 float f0 = expf((log(f / 440) / log2 * 24 - 1) / 24.0f*log2) * 440;
591 maxima0[i] = f2pix(f0);
592 float f1 = expf((log(f / 440) / log2 * 24 + 1) / 24.0f*log2) * 440;
593 maxima1[i] = f2pix(f1);
594 }
595 }
596
597 int it = 0;
598 bool inMaximum = false;
599#endif //EXPERIMENTAL_FIND_NOTES
600
601 for (int yy = 0; yy < hiddenMid.height; ++yy) {
602 const float bin = bins[yy];
603 const float nextBin = bins[yy+1];
604
606 const float value = findValue
607 (freq + nBins * xx, bin, nextBin, nBins, autocorrelation, gain, range);
608 clipCache.mSpecPxCache->values[xx * hiddenMid.height + yy] = value;
609 }
610 else {
611 float value;
612
613#ifdef EXPERIMENTAL_FIND_NOTES
614 if (fftFindNotes) {
615 if (it < maximas) {
616 float i0 = maxima0[it];
617 if (yy >= i0)
618 inMaximum = true;
619
620 if (inMaximum) {
621 float i1 = maxima1[it];
622 if (yy + 1 <= i1) {
623 value = findValue(freq + x0, bin, nextBin, nBins, autocorrelation, gain, range);
624 if (value < findNotesMinA)
625 value = minColor;
626 }
627 else {
628 it++;
629 inMaximum = false;
630 value = minColor;
631 }
632 }
633 else {
634 value = minColor;
635 }
636 }
637 else
638 value = minColor;
639 }
640 else
641#endif //EXPERIMENTAL_FIND_NOTES
642 {
643 value = findValue
644 (freq + nBins * xx, bin, nextBin, nBins, autocorrelation, gain, range);
645 }
646 clipCache.mSpecPxCache->values[xx * hiddenMid.height + yy] = value;
647 } // logF
648 } // each yy
649 } // each xx
650 } // updating cache
651
652 float selBinLo = settings.findBin( freqLo, binUnit);
653 float selBinHi = settings.findBin( freqHi, binUnit);
654 float selBinCenter = (freqLo < 0 || freqHi < 0)
655 ? -1
656 : settings.findBin( sqrt(freqLo * freqHi), binUnit );
657
658 const bool isSpectral = settings.SpectralSelectionEnabled();
659 const bool hidden = (ZoomInfo::HIDDEN == zoomInfo.GetFisheyeState());
660 const int begin = hidden
661 ? 0
662 : std::max(0, (int)(zoomInfo.GetFisheyeLeftBoundary(-leftOffset)));
663 const int end = hidden
664 ? 0
665 : std::min(mid.width, (int)(zoomInfo.GetFisheyeRightBoundary(-leftOffset)));
666 const size_t numPixels = std::max(0, end - begin);
667
668 SpecCache specCache;
669
670 // need explicit resize since specCache.where[] accessed before Populate()
671 specCache.Grow(numPixels, settings, -1, t0);
672
673 if (numPixels > 0) {
674 for (int ii = begin; ii < end; ++ii) {
675 const double time = zoomInfo.PositionToTime(ii, -leftOffset) - tOffset;
676 specCache.where[ii - begin] = sampleCount(0.5 + rate * time);
677 }
678 specCache.Populate
679 (settings, waveTrackCache,
680 0, 0, numPixels,
681 clip->GetPlaySamplesCount(),
682 tOffset, rate,
683 0 // FIXME: PRL -- make reassignment work with fisheye
684 );
685 }
686
687 // build color gradient tables (not thread safe)
690
691 // left pixel column of the fisheye
692 int fisheyeLeft = zoomInfo.GetFisheyeLeftBoundary(-leftOffset);
693
694 // Bug 2389 - always draw at least one pixel of selection.
695 int selectedX = zoomInfo.TimeToPosition(selectedRegion.t0(), -leftOffset);
696
697#ifdef _OPENMP
698#pragma omp parallel for
699#endif
700
701 const NumberScale numberScale(settings.GetScale(minFreq, maxFreq));
702 int windowSize = mpSpectralData->GetWindowSize();
703 int hopSize = mpSpectralData->GetHopSize();
704 double sr = mpSpectralData->GetSR();
705 auto &dataHistory = mpSpectralData->dataHistory;
706
707 // Lazy way to add all hops and bins required for rendering
708 dataHistory.push_back(mpSpectralData->dataBuffer);
709
710 // Generate combined hops and bins map for rendering
711 std::map<long long, std::set<int>> hopBinMap;
712 for(auto vecIter = dataHistory.begin(); vecIter != dataHistory.end(); ++vecIter){
713 for(const auto &hopMap: *vecIter){
714 for(const auto &binNum: hopMap.second)
715 hopBinMap[hopMap.first].insert(binNum);
716 }
717 }
718
719 // Lambda for converting yy (not mouse coord!) to respective freq. bins
720 auto yyToFreqBin = [&](int yy){
721 const double p = double(yy) / hiddenMid.height;
722 float convertedFreq = numberScale.PositionToValue(p);
723 float convertedFreqBinNum = convertedFreq / (sr / windowSize);
724
725 // By default lrintf will round to nearest by default, rounding to even on tie.
726 // std::round that was used here before rounds halfway cases away from zero.
727 // However, we can probably tolerate rounding issues here, as this will only slightly affect
728 // the visuals.
729 return static_cast<int>(lrintf(convertedFreqBinNum));
730 };
731
732 for (int xx = 0; xx < mid.width; ++xx) {
733 int correctedX = xx + leftOffset - hiddenLeftOffset;
734
735 // in fisheye mode the time scale has changed, so the row values aren't cached
736 // in the loop above, and must be fetched from fft cache
737 float* uncached;
738 if (!zoomInfo.InFisheye(xx, -leftOffset)) {
739 uncached = 0;
740 }
741 else {
742 int specIndex = (xx - fisheyeLeft) * nBins;
743 wxASSERT(specIndex >= 0 && specIndex < (int)specCache.freq.size());
744 uncached = &specCache.freq[specIndex];
745 }
746
747 // zoomInfo must be queried for each column since with fisheye enabled
748 // time between columns is variable
749 auto w0 = sampleCount(0.5 + rate *
750 (zoomInfo.PositionToTime(xx, -leftOffset) - tOffset));
751
752 auto w1 = sampleCount(0.5 + rate *
753 (zoomInfo.PositionToTime(xx+1, -leftOffset) - tOffset));
754
755 bool maybeSelected = ssel0 <= w0 && w1 < ssel1;
756 maybeSelected = maybeSelected || (xx == selectedX);
757
758 // In case the xx matches the hop number, it will be used as iterator for frequency bins
759 std::set<int> *pSelectedBins = nullptr;
760 std::set<int>::iterator freqBinIter;
761 auto advanceFreqBinIter = [&](int nextBinRounded){
762 while (freqBinIter != pSelectedBins->end() &&
763 *freqBinIter < nextBinRounded)
764 ++freqBinIter;
765 };
766
767 bool hitHopNum = false;
768 if (onBrushTool) {
769 int convertedHopNum = (w0.as_long_long() + hopSize / 2) / hopSize;
770 hitHopNum = (hopBinMap.find(convertedHopNum) != hopBinMap.end());
771 if(hitHopNum) {
772 pSelectedBins = &hopBinMap[convertedHopNum];
773 freqBinIter = pSelectedBins->begin();
774 advanceFreqBinIter(yyToFreqBin(0));
775 }
776 }
777
778 for (int yy = 0; yy < hiddenMid.height; ++yy) {
779 if(onBrushTool)
780 maybeSelected = false;
781 const float bin = bins[yy];
782 const float nextBin = bins[yy+1];
783 auto binRounded = yyToFreqBin(yy);
784 auto nextBinRounded = yyToFreqBin(yy + 1);
785
786 if(hitHopNum
787 && freqBinIter != pSelectedBins->end()
788 && binRounded == *freqBinIter)
789 maybeSelected = true;
790
791 if (hitHopNum)
792 advanceFreqBinIter(nextBinRounded);
793
794 // For spectral selection, determine what colour
795 // set to use. We use a darker selection if
796 // in both spectral range and time range.
797
799
800 // If we are in the time selected range, then we may use a different color set.
801 if (maybeSelected) {
802 selected =
803 ChooseColorSet(bin, nextBin, selBinLo, selBinCenter, selBinHi,
804 (xx + leftOffset - hiddenLeftOffset) / DASH_LENGTH, isSpectral);
805 if ( onBrushTool && selected != AColor::ColorGradientUnselected )
806 // use only two sets of colors
808 }
809
810 const float value = uncached
811 ? findValue(uncached, bin, nextBin, nBins, autocorrelation, gain, range)
812 : clipCache.mSpecPxCache->values[correctedX * hiddenMid.height + yy];
813
814 unsigned char rv, gv, bv;
815 GetColorGradient(value, selected, colorScheme, &rv, &gv, &bv);
816
817#ifdef EXPERIMENTAL_FFT_Y_GRID
818 if (fftYGrid && yGrid[yy]) {
819 rv /= 1.1f;
820 gv /= 1.1f;
821 bv /= 1.1f;
822 }
823#endif //EXPERIMENTAL_FFT_Y_GRID
824 int px = ((mid.height - 1 - yy) * mid.width + xx);
825#ifdef EXPERIMENTAL_SPECTROGRAM_OVERLAY
826 // More transparent the closer to zero intensity.
827 alpha[px]= wxMin( 200, (value+0.3) * 500) ;
828#endif
829 px *=3;
830 data[px++] = rv;
831 data[px++] = gv;
832 data[px] = bv;
833 } // each yy
834 } // each xx
835
836 dataHistory.pop_back();
837 wxBitmap converted = wxBitmap(image);
838
839 wxMemoryDC memDC;
840
841 memDC.SelectObject(converted);
842
843 dc.Blit(mid.x, mid.y, mid.width, mid.height, &memDC, 0, 0, wxCOPY, FALSE);
844
845 // Draw clip edges, as also in waveform view, which improves the appearance
846 // of split views
847 {
848 auto clipRect = ClipParameters::GetClipRect(*clip, zoomInfo, rect);
849 TrackArt::DrawClipEdges(dc, clipRect, selected);
850 }
851}
852
853}
854
856 const WaveTrack* track,
857 const WaveClip* selectedClip,
858 const wxRect & rect )
859{
860 const auto artist = TrackArtist::Get( context );
861 const auto &blankSelectedBrush = artist->blankSelectedBrush;
862 const auto &blankBrush = artist->blankBrush;
864 context, rect, track, blankSelectedBrush, blankBrush );
865
866 SampleTrackCache cache(track->SharedPointer<const WaveTrack>());
867 for (const auto &clip: track->GetClips()){
868 DrawClipSpectrum( context, cache, clip.get(), rect,
869 mpSpectralData, clip.get() == selectedClip);
870 }
871
872 DrawBoldBoundaries( context, track, rect );
873}
874
876 TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass )
877{
878 if ( iPass == TrackArtist::PassTracks ) {
879 auto &dc = context.dc;
880 // Update cache for locations, e.g. cutlines and merge points
881 // Bug2588: do this for both channels, even if one is not drawn, so that
882 // cut-line editing (which depends on the locations cache) works properly.
883 // If both channels are visible, we will duplicate this effort, but that
884 // matters little.
885 for( auto channel:
886 TrackList::Channels(static_cast<WaveTrack*>(FindTrack().get())) ) {
887 auto &locationsCache = WaveTrackLocations::Get( *channel );
888 locationsCache.Update( *channel );
889 }
890
891 const auto wt = std::static_pointer_cast<const WaveTrack>(
892 FindTrack()->SubstitutePendingChangedTrack());
893
894 const auto artist = TrackArtist::Get( context );
895
896#if defined(__WXMAC__)
897 wxAntialiasMode aamode = dc.GetGraphicsContext()->GetAntialiasMode();
898 dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE);
899#endif
900
901 auto waveTrackView = GetWaveTrackView().lock();
902 wxASSERT(waveTrackView.use_count());
903
904 auto seletedClip = waveTrackView->GetSelectedClip().lock();
905 DoDraw( context, wt.get(), seletedClip.get(), rect );
906
907#if defined(__WXMAC__)
908 dc.GetGraphicsContext()->SetAntialiasMode(aamode);
909#endif
910 }
911 WaveTrackSubView::Draw( context, rect, iPass );
912}
913
915 []( WaveTrackView &view ){
916 return std::make_shared< SpectrumView >( view );
917 }
918};
919
920// The following attaches the spectrogram settings item to the wave track popup
921// menu. It is appropriate only to spectrum view and so is kept in this
922// source file with the rest of the spectrum view implementation.
923#include "WaveTrackControls.h"
924#include "AudioIOBase.h"
925#include "../../../../Menus.h"
926#include "ProjectHistory.h"
927#include "../../../../RefreshCode.h"
928#include "../../../../prefs/PrefsDialog.h"
929#include "../../../../prefs/SpectrumPrefs.h"
930#include "AudacityMessageBox.h"
931#include "../../../../widgets/PopupMenuTable.h"
932
933namespace {
935
938 {
939 static SpectrogramSettingsHandler instance;
940 return instance;
941 }
942
943 void OnSpectrogramSettings(wxCommandEvent &);
944
945 void InitUserData(void *pUserData) override
946 {
947 mpData = static_cast< PlayableTrackControls::InitMenuData* >(pUserData);
948 }
949};
950
951void SpectrogramSettingsHandler::OnSpectrogramSettings(wxCommandEvent &)
952{
953 class ViewSettingsDialog final : public PrefsDialog
954 {
955 public:
956 ViewSettingsDialog(wxWindow *parent, AudacityProject &project,
958 int page)
959 : PrefsDialog(parent, &project, title, factories)
960 , mPage(page)
961 {
962 }
963
964 long GetPreferredPage() override
965 {
966 return mPage;
967 }
968
969 void SavePreferredPage() override
970 {
971 }
972
973 private:
974 const int mPage;
975 };
976
977 auto gAudioIO = AudioIOBase::Get();
978 if (gAudioIO->IsBusy()){
980 XO(
981"To change Spectrogram Settings, stop any\n playing or recording first."),
982 XO("Stop the Audio First"),
983 wxOK | wxICON_EXCLAMATION | wxCENTRE);
984 return;
985 }
986
987 WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
988
989 PrefsPanel::Factories factories;
990 // factories.push_back(WaveformPrefsFactory( pTrack ));
991 factories.push_back(SpectrumPrefsFactory( pTrack ));
992 const int page =
993 // (pTrack->GetDisplay() == WaveTrackViewConstants::Spectrum) ? 1 :
994 0;
995
996 auto title = XO("%s:").Format( pTrack->GetName() );
997 ViewSettingsDialog dialog(
998 mpData->pParent, mpData->project, title, factories, page);
999
1000 if (0 != dialog.ShowModal()) {
1001 // Redraw
1002 AudacityProject *const project = &mpData->project;
1003 ProjectHistory::Get( *project ).ModifyState(true);
1004 //Bug 1725 Toolbar was left greyed out.
1005 //This solution is overkill, but does fix the problem and is what the
1006 //prefs dialog normally does.
1008 mpData->result = RefreshCode::RefreshAll;
1009 }
1010}
1011
1014 { "SubViews/Extra" },
1015 std::make_unique<PopupMenuSection>( "SpectrogramSettings",
1016 // Conditionally add menu item for settings, if showing spectrum
1017 PopupMenuTable::Computed< WaveTrackPopupMenuTable >(
1019 using Entry = PopupMenuTable::Entry;
1020 static const int OnSpectrogramSettingsID =
1022
1023 const auto pTrack = &table.FindWaveTrack();
1024 const auto &view = WaveTrackView::Get( *pTrack );
1025 const auto displays = view.GetDisplays();
1026 bool hasSpectrum = (displays.end() != std::find(
1027 displays.begin(), displays.end(),
1028 WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} }
1029 ) );
1030 if( hasSpectrum )
1031 // In future, we might move this to the context menu of the
1032 // Spectrum vertical ruler.
1033 // (But the latter won't be satisfactory without a means to
1034 // open that other context menu with keystrokes only, and that
1035 // would require some notion of a focused sub-view.)
1036 return std::make_unique<Entry>( "SpectrogramSettings",
1037 Entry::Item,
1038 OnSpectrogramSettingsID,
1039 XXO("S&pectrogram Settings..."),
1040 (wxCommandEventFunction)
1041 (&SpectrogramSettingsHandler::OnSpectrogramSettings),
1042 SpectrogramSettingsHandler::Instance(),
1043 []( PopupMenuHandler &handler, wxMenu &menu, int id ){
1044 // Bug 1253. Shouldn't open preferences if audio is busy.
1045 // We can't change them on the fly yet anyway.
1046 auto gAudioIO = AudioIOBase::Get();
1047 menu.Enable(id, !gAudioIO->IsBusy());
1048 } );
1049 else
1050 return nullptr;
1051 } ) )
1052};
1053}
1054
1055static bool ShouldCaptureEvent(wxKeyEvent& event, SpectralData *pData)
1056{
1057 const auto keyCode = event.GetKeyCode();
1058 return
1059 (keyCode == WXK_BACK || keyCode == WXK_DELETE ||
1060 keyCode == WXK_NUMPAD_DELETE)
1061 && pData && !pData->dataHistory.empty();
1062}
1063
1065 wxKeyEvent& event, ViewInfo&, wxWindow*, AudacityProject*)
1066{
1067 bool capture = ShouldCaptureEvent(event, mpSpectralData.get());
1068 event.Skip(!capture);
1070}
1071
1072unsigned SpectrumView::KeyDown(wxKeyEvent& event, ViewInfo& viewInfo, wxWindow*, AudacityProject* project)
1073{
1074 bool capture = ShouldCaptureEvent(event, mpSpectralData.get());
1075 event.Skip(!capture);
1076 if (capture && SpectralDataManager::ProcessTracks(*project))
1077 // Not RefreshCell, because there might be effects in multiple tracks
1080}
1081
1083 wxKeyEvent &event, ViewInfo&, wxWindow*, AudacityProject* )
1084{
1085 bool capture = ShouldCaptureEvent(event, mpSpectralData.get());
1086 event.Skip(!capture);
1088}
1089
1090#ifdef EXPERIMENTAL_SPECTRAL_EDITING
1091// Attach some related menu items
1092#include "../../../ui/SelectHandle.h"
1093#include "../../../../CommonCommandFlags.h"
1094#include "Project.h"
1095#include "../../../../SpectrumAnalyst.h"
1096#include "../../../../commands/CommandContext.h"
1097
1098namespace {
1099void DoNextPeakFrequency(AudacityProject &project, bool up)
1100{
1101 auto &tracks = TrackList::Get( project );
1102 auto &viewInfo = ViewInfo::Get( project );
1103
1104 // Find the first selected wave track that is in a spectrogram view.
1105 const WaveTrack *pTrack {};
1106 for ( auto wt : tracks.Selected< const WaveTrack >() ) {
1107 const auto displays = WaveTrackView::Get( *wt ).GetDisplays();
1108 bool hasSpectrum = (displays.end() != std::find(
1109 displays.begin(), displays.end(),
1110 WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} }
1111 ) );
1112 if ( hasSpectrum ) {
1113 pTrack = wt;
1114 break;
1115 }
1116 }
1117
1118 if (pTrack) {
1119 SpectrumAnalyst analyst;
1120 SelectHandle::SnapCenterOnce(analyst, viewInfo, pTrack, up);
1121 ProjectHistory::Get( project ).ModifyState(false);
1122 }
1123}
1124
1125struct Handler : CommandHandlerObject, ClientData::Base {
1126
1127// Handler state:
1128double mLastF0{ SelectedRegion::UndefinedFrequency };
1129double mLastF1{ SelectedRegion::UndefinedFrequency };
1130
1131void OnToggleSpectralSelection(const CommandContext &context)
1132{
1133 auto &project = context.project;
1134 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
1135
1136 const double f0 = selectedRegion.f0();
1137 const double f1 = selectedRegion.f1();
1138 const bool haveSpectralSelection =
1141 if (haveSpectralSelection)
1142 {
1143 mLastF0 = f0;
1144 mLastF1 = f1;
1145 selectedRegion.setFrequencies
1147 }
1148 else
1149 selectedRegion.setFrequencies(mLastF0, mLastF1);
1150
1151 ProjectHistory::Get( project ).ModifyState(false);
1152}
1153
1154void OnNextHigherPeakFrequency(const CommandContext &context)
1155{
1156 auto &project = context.project;
1157 DoNextPeakFrequency(project, true);
1158}
1159
1160void OnNextLowerPeakFrequency(const CommandContext &context)
1161{
1162 auto &project = context.project;
1163 DoNextPeakFrequency(project, false);
1164}
1165};
1166
1167// Handler is stateful. Needs a factory registered with
1168// AudacityProject.
1170 [](AudacityProject&) {
1171 return std::make_unique< Handler >(); } };
1172
1174 return project.AttachedObjects::Get< Handler >( key );
1175};
1176
1177using namespace MenuTable;
1178#define FN(X) (& Handler :: X)
1179
1180BaseItemSharedPtr SpectralSelectionMenu()
1181{
1182 static BaseItemSharedPtr menu{
1184 Menu( wxT("Spectral"), XXO("S&pectral"),
1185 Command( wxT("ToggleSpectralSelection"),
1186 XXO("To&ggle Spectral Selection"), FN(OnToggleSpectralSelection),
1187 TracksExistFlag(), wxT("Q") ),
1188 Command( wxT("NextHigherPeakFrequency"),
1189 XXO("Next &Higher Peak Frequency"), FN(OnNextHigherPeakFrequency),
1190 TracksExistFlag() ),
1191 Command( wxT("NextLowerPeakFrequency"),
1192 XXO("Next &Lower Peak Frequency"), FN(OnNextLowerPeakFrequency),
1193 TracksExistFlag() )
1194 ) ) };
1195 return menu;
1196}
1197
1198#undef FN
1199
1201 Placement{ wxT("Select/Basic"), { OrderingHint::After, wxT("Region") } },
1202 Indirect(SpectralSelectionMenu())
1203};
1204
1205}
1206#endif // EXPERIMENTAL_SPECTRAL_EDITING
void GetColorGradient(float value, AColor::ColorGradientChoice selected, int colorScheme, unsigned char *__restrict red, unsigned char *__restrict green, unsigned char *__restrict blue)
Definition: AColor.h:151
wxImage(22, 22)
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
AttachedItem sAttachment2
std::shared_ptr< UIHandle > UIHandlePtr
Definition: CellularPanel.h:28
wxEvtHandler CommandHandlerObject
const ReservedCommandFlag & TracksExistFlag()
int min(int a, int b)
EffectDistortionSettings params
Definition: Distortion.cpp:77
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define FN(X)
static CommandHandlerObject & findCommandHandler(AudacityProject &project)
static const auto title
FileConfig * gPrefs
Definition: Prefs.cpp:70
PrefsPanel::Factory SpectrumPrefsFactory(WaveTrack *wt)
static bool ShouldCaptureEvent(wxKeyEvent &event, SpectralData *pData)
static WaveTrackSubView::Type sType
static WaveTrackSubViewType::RegisteredType reg
static UIHandlePtr BrushHandleHitTest(std::weak_ptr< BrushHandle > &holder, const TrackPanelMouseState &st, const AudacityProject *pProject, const std::shared_ptr< SpectrumView > &pTrackView, const std::shared_ptr< SpectralData > &mpData)
static const WaveTrackSubViews::RegisteredFactory key
static Settings & settings()
Definition: TrackInfo.cpp:83
std::shared_ptr< Subclass > AssignUIHandlePtr(std::weak_ptr< Subclass > &holder, const std::shared_ptr< Subclass > &pNew)
Definition: UIHandle.h:151
WaveTrackPopupMenuTable & GetWaveTrackMenuTable()
static const auto fn
ColorGradientChoice
Definition: AColor.h:28
@ ColorGradientUnselected
Definition: AColor.h:29
@ ColorGradientTimeAndFrequencySelected
Definition: AColor.h:31
@ ColorGradientEdge
Definition: AColor.h:32
@ ColorGradientTimeSelected
Definition: AColor.h:30
static void PreComputeGradient()
Definition: AColor.cpp:721
static bool gradient_inited
Definition: AColor.h:135
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:93
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
AudacityProject & project
std::shared_ptr< Track > FindTrack()
static void RebuildAllMenuBars()
Definition: Menus.cpp:625
double f0() const
Definition: ViewInfo.h:37
float PositionToValue(float pp) const
Definition: NumberScale.h:155
Iterator begin(float nPositions) const
Definition: NumberScale.h:232
PopupMenuTableEntry Entry
Dialog that shows the current PrefsPanel in a tabbed divider.
Definition: PrefsDialog.h:29
virtual void SavePreferredPage()=0
PrefsDialog(wxWindow *parent, AudacityProject *pProject, const TranslatableString &titlePrefix=XO("Preferences:"), PrefsPanel::Factories &factories=PrefsPanel::DefaultFactories())
virtual long GetPreferredPage()=0
std::vector< PrefsPanel::PrefsNode > Factories
Definition: PrefsPanel.h:72
A simple profiler to measure the average time lengths that a particular task/function takes....
Definition: Profiler.h:40
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
A short-lived object, during whose lifetime, the contents of the WaveTrack are assumed not to change.
const std::shared_ptr< const SampleTrack > & GetTrack() const
static void SnapCenterOnce(SpectrumAnalyst &analyst, ViewInfo &viewInfo, const WaveTrack *pTrack, bool up)
static const int UndefinedFrequency
void Populate(const SpectrogramSettings &settings, SampleTrackCache &waveTrackCache, int copyBegin, int copyEnd, size_t numPixels, sampleCount numSamples, double offset, double rate, double pixelsPerSecond)
void Grow(size_t len_, SpectrogramSettings &settings, double pixelsPerSecond, double start_)
std::vector< float > freq
Definition: SpectrumCache.h:77
std::vector< sampleCount > where
Definition: SpectrumCache.h:78
std::vector< HopsAndBinsMap > dataHistory
Definition: SpectrumView.h:44
static bool ProcessTracks(AudacityProject &project)
void GetBounds(const WaveTrack &wt, float &min, float &max) const
void SetBounds(float min, float max)
static SpectrogramBounds & Get(WaveTrack &track)
Get either the global default settings, or the track's own if previously created.
static SpectrogramSettings & Get(const WaveTrack &track)
Mutative access to attachment even if the track argument is const.
Used for finding the peaks, for snapping to peaks.
SpectralDataSaver(SpectrumView &view)
void Init(AudacityProject &project, bool clearAll) override
~SpectrumView() override
static void ForAll(AudacityProject &project, std::function< void(SpectrumView &view)> fn)
void DoSetMinimized(bool minimized) override
unsigned CaptureKey(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent, AudacityProject *project) override
void DoDraw(TrackPanelDrawingContext &context, const WaveTrack *track, const WaveClip *selectedClip, const wxRect &rect)
bool IsSpectral() const override
std::shared_ptr< TrackVRulerControls > DoGetVRulerControls() override
std::shared_ptr< SpectralData > mpSpectralData
Definition: SpectrumView.h:137
std::weak_ptr< BrushHandle > mBrushHandle
Definition: SpectrumView.h:133
unsigned KeyDown(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent, AudacityProject *project) override
std::shared_ptr< SpectralData > mpBackupSpectralData
Definition: SpectrumView.h:137
const Type & SubViewType() const override
unsigned Char(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent, AudacityProject *project) override
std::shared_ptr< SpectralData > GetSpectralData()
void CopyToSubView(WaveTrackSubView *destSubView) const override
std::vector< UIHandlePtr > DetailedHitTest(const TrackPanelMouseState &state, const AudacityProject *pProject, int currentTool, bool bMultiTool) override
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
SpectrumView(WaveTrackView &waveTrackView, const SpectrumView &src)=delete
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:69
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:232
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:68
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:385
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1417
virtual void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass)
virtual void DoSetMinimized(bool isMinimized)
Definition: TrackView.cpp:141
static TrackView & Get(Track &)
Definition: TrackView.cpp:69
Holds a msgid for the translation catalog; may also bind format arguments.
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:219
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:101
sampleCount GetPlaySamplesCount() const
Definition: WaveClip.cpp:921
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
WaveClipHolders & GetClips()
Definition: WaveTrack.h:322
const std::vector< Location > & Get() const
static void DrawBoldBoundaries(TrackPanelDrawingContext &context, const WaveTrack *track, const wxRect &rect)
std::pair< bool, std::vector< UIHandlePtr > > DoDetailedHitTest(const TrackPanelMouseState &state, const AudacityProject *pProject, int currentTool, bool bMultiTool, const std::shared_ptr< WaveTrack > &wt)
std::weak_ptr< WaveTrackView > GetWaveTrackView() const
static WaveTrackView & Get(WaveTrack &track)
std::vector< WaveTrackSubView::Type > GetDisplays() const
static bool ClipDetailsVisible(const WaveClip &clip, const ZoomInfo &zoomInfo, const wxRect &viewRect)
@ HIDDEN
Definition: ZoomInfo.h:149
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
#define lrintf(flt)
Definition: float_cast.h:170
constexpr auto Menu
constexpr auto Command
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
std::unique_ptr< BaseItem > BaseItemPtr
Definition: Registry.h:73
std::unique_ptr< IndirectItem > Indirect(const BaseItemSharedPtr &ptr)
A convenience function.
Definition: Registry.h:97
std::shared_ptr< BaseItem > BaseItemSharedPtr
Definition: Registry.h:74
AUDACITY_DLL_API void DrawClipFolded(wxDC &dc, const wxRect &rect)
Definition: TrackArt.cpp:293
AUDACITY_DLL_API void DrawClipEdges(wxDC &dc, const wxRect &clipRect, bool selected=false)
Definition: TrackArt.cpp:256
AUDACITY_DLL_API void DrawBackgroundWithSelection(TrackPanelDrawingContext &context, const wxRect &rect, const Track *track, const wxBrush &selBrush, const wxBrush &unselBrush, bool useSelection=true)
Definition: TrackArt.cpp:422
PopupMenuTable::AttachedItem sAttachment
AColor::ColorGradientChoice ChooseColorSet(float bin0, float bin1, float selBinLo, float selBinCenter, float selBinHi, int dashCount, bool isSpectral)
static float findValue(const float *spectrum, float bin0, float bin1, unsigned nBins, bool autocorrelation, int gain, int range)
void DrawClipSpectrum(TrackPanelDrawingContext &context, SampleTrackCache &waveTrackCache, const WaveClip *clip, const wxRect &rect, const std::shared_ptr< SpectralData > &mpSpectralData, bool selected)
__finl float_x4 __vecc sqrt(const float_x4 &a)
A convenient default parameter for class template Site.
Definition: ClientData.h:28
static wxRect GetClipRect(const WaveClip &clip, const ZoomInfo &zoomInfo, const wxRect &viewRect, bool *outShowSamples=nullptr)
static WaveClipSpectrumCache & Get(const WaveClip &clip)
bool GetSpectrogram(const WaveClip &clip, SampleTrackCache &cache, const float *&spectrogram, const sampleCount *&where, size_t numPixels, double t0, double pixelsPerSecond)
WaveTrack & FindWaveTrack() const
void InitUserData(void *pUserData) override
Called before the menu items are appended.