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 WaveChannelView.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
20#include "ClipParameters.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 "WaveTrackLocation.h"
37
38#include <wx/dcmemory.h>
39#include <wx/graphics.h>
40
41#include "float_cast.h"
42
43class BrushHandle;
44class SpectralData;
45
48 { wxT("Spectrogram"), XXO("&Spectrogram") }
49};
50
52
54 : WaveChannelSubView(waveChannelView)
55{
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 {
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> &pChannelView,
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>(*pChannelView),
133 pChannelView, 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 auto pTrack = pChannelView->FindTrack();
140 if (!pTrack->GetSelected())
141 {
142 return result;
143 }
144
145 return result;
146}
147
149 std::function<void(SpectrumView &view)> fn )
150{
151 if (!fn)
152 return;
153 for (const auto wt : TrackList::Get(project).Any<WaveTrack>()) {
154 for (auto pChannel : wt->Channels()) {
155 if (auto pWaveChannelView =
156 dynamic_cast<WaveChannelView*>(&ChannelView::Get(*pChannel))) {
157 for (const auto &pSubView : pWaveChannelView->GetAllSubViews()) {
158 if (const auto sView = dynamic_cast<SpectrumView*>(pSubView.get()))
159 fn( *sView );
160 }
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 sampleRate 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
214}
215
216auto SpectrumView::SubViewType() const -> const Type &
217{
218 return sType;
219}
220
221std::shared_ptr<ChannelVRulerControls> 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{
232 if (const auto pDest = dynamic_cast< SpectrumView* >(destSubView)) {
233 pDest->mpSpectralData =
234 std::make_shared<SpectralData>(mpSpectralData->GetSR());
235 pDest->mpSpectralData->CopyFrom(*mpSpectralData);
236 }
237}
238
239namespace
240{
241
242static inline float findValue
243(const float *spectrum, float bin0, float bin1, unsigned nBins,
244 bool autocorrelation, int gain, int range)
245{
246 float value;
247
248
249#if 0
250 // Averaging method
251 if ((int)(bin1) == (int)(bin0)) {
252 value = spectrum[(int)(bin0)];
253 } else {
254 float binwidth= bin1 - bin0;
255 value = spectrum[(int)(bin0)] * (1.f - bin0 + (int)bin0);
256
257 bin0 = 1 + (int)(bin0);
258 while (bin0 < (int)(bin1)) {
259 value += spectrum[(int)(bin0)];
260 bin0 += 1.0;
261 }
262 // Do not reference past end of freq array.
263 if ((int)(bin1) >= (int)nBins) {
264 bin1 -= 1.0;
265 }
266
267 value += spectrum[(int)(bin1)] * (bin1 - (int)(bin1));
268 value /= binwidth;
269 }
270#else
271 // Maximum method, and no apportionment of any single bins over multiple pixel rows
272 // See Bug971
273 int index, limitIndex;
274 if (autocorrelation) {
275 // bin = 2 * nBins / (nBins - 1 - array_index);
276 // Solve for index
277 index = std::max(0.0f, std::min(float(nBins - 1),
278 (nBins - 1) - (2 * nBins) / (std::max(1.0f, bin0))
279 ));
280 limitIndex = std::max(0.0f, std::min(float(nBins - 1),
281 (nBins - 1) - (2 * nBins) / (std::max(1.0f, bin1))
282 ));
283 }
284 else {
285 index = std::min<int>(nBins - 1, (int)(floor(0.5 + bin0)));
286 limitIndex = std::min<int>(nBins, (int)(floor(0.5 + bin1)));
287 }
288 value = spectrum[index];
289 while (++index < limitIndex)
290 value = std::max(value, spectrum[index]);
291#endif
292 if (!autocorrelation) {
293 // Last step converts dB to a 0.0-1.0 range
294 value = (value + range + gain) / (double)range;
295 }
296 value = std::min(1.0f, std::max(0.0f, value));
297 return value;
298}
299
300// dashCount counts both dashes and the spaces between them.
302ChooseColorSet( float bin0, float bin1, float selBinLo,
303 float selBinCenter, float selBinHi, int dashCount, bool isSpectral )
304{
305 if (!isSpectral)
307 if ((selBinCenter >= 0) && (bin0 <= selBinCenter) &&
308 (selBinCenter < bin1))
310 if ((0 == dashCount % 2) &&
311 (((selBinLo >= 0) && (bin0 <= selBinLo) && ( selBinLo < bin1)) ||
312 ((selBinHi >= 0) && (bin0 <= selBinHi) && ( selBinHi < bin1))))
314 if ((selBinLo < 0 || selBinLo < bin1) && (selBinHi < 0 || selBinHi > bin0))
316
318}
319
320std::pair<sampleCount, sampleCount> GetSelectedSampleIndices(
321 const SelectedRegion& selectedRegion, const WaveChannelInterval& clip,
322 bool trackIsSelected)
323{
324 if (!trackIsSelected)
325 return { 0, 0 };
326 const double t0 = selectedRegion.t0(); // left selection bound
327 const double t1 = selectedRegion.t1(); // right selection bound
328 const auto startTime = clip.GetPlayStartTime();
329 const auto s0 = std::max(sampleCount(0), clip.TimeToSamples(t0 - startTime));
330 auto s1 = std::clamp(
331 clip.TimeToSamples(t1 - startTime), sampleCount { 0 },
332 clip.GetVisibleSampleCount());
333 return { s0, s1 };
334}
335
337 const WaveChannelInterval &clip, const wxRect &rect,
338 const std::shared_ptr<SpectralData> &mpSpectralData,
339 bool selected)
340{
341 auto &dc = context.dc;
342 const auto artist = TrackArtist::Get(context);
343 bool onBrushTool = artist->onBrushTool;
344 const auto &selectedRegion = *artist->pSelectedRegion;
345 const auto &zoomInfo = *artist->pZoomInfo;
346
347#ifdef PROFILE_WAVEFORM
348 Profiler profiler;
349#endif
350
351 //If clip is "too small" draw a placeholder instead of
352 //attempting to fit the contents into a few pixels
353 if (!WaveChannelView::ClipDetailsVisible(clip, zoomInfo, rect))
354 {
355 auto clipRect = ClipParameters::GetClipRect(clip, zoomInfo, rect);
356 TrackArt::DrawClipFolded(dc, clipRect);
357 return;
358 }
359
360 auto &settings = SpectrogramSettings::Get(track);
361 const bool autocorrelation = (settings.algorithm == SpectrogramSettings::algPitchEAC);
362
363 enum { DASH_LENGTH = 10 /* pixels */ };
364
365 const ClipParameters params { clip, rect, zoomInfo };
366 const wxRect &hiddenMid = params.hiddenMid;
367 // The "hiddenMid" rect contains the part of the display actually
368 // containing the waveform, as it appears without the fisheye. If it's empty, we're done.
369 if (hiddenMid.width <= 0) {
370 return;
371 }
372
373 const double &t0 = params.t0;
374 const double playStartTime = clip.GetPlayStartTime();
375 const auto [ssel0, ssel1] =
376 GetSelectedSampleIndices(selectedRegion, clip, track.GetSelected());
377 const double &averagePixelsPerSecond = params.averagePixelsPerSecond;
378 const double sampleRate = clip.GetRate();
379 const double stretchRatio = clip.GetStretchRatio();
380 const double &hiddenLeftOffset = params.hiddenLeftOffset;
381 const double &leftOffset = params.leftOffset;
382 const wxRect &mid = params.mid;
383
386#ifdef EXPERIMENTAL_SPECTRAL_EDITING
387 freqLo = selectedRegion.f0();
388 freqHi = selectedRegion.f1();
389#endif
390
391 const int &colorScheme = settings.colorScheme;
392 const int &range = settings.range;
393 const int &gain = settings.gain;
394
395#ifdef EXPERIMENTAL_FIND_NOTES
396 const bool &fftFindNotes = settings.fftFindNotes;
397 const double &findNotesMinA = settings.findNotesMinA;
398 const int &numberOfMaxima = settings.numberOfMaxima;
399 const bool &findNotesQuantize = settings.findNotesQuantize;
400#endif
401#ifdef EXPERIMENTAL_FFT_Y_GRID
402 const bool &fftYGrid = settings.fftYGrid;
403#endif
404
405 dc.SetPen(*wxTRANSPARENT_PEN);
406
407 // We draw directly to a bit image in memory,
408 // and then paint this directly to our offscreen
409 // bitmap. Note that this could be optimized even
410 // more, but for now this is not bad. -dmazzoni
411 wxImage image((int)mid.width, (int)mid.height);
412 if (!image.IsOk())
413 return;
414#ifdef EXPERIMENTAL_SPECTROGRAM_OVERLAY
415 image.SetAlpha();
416 unsigned char *alpha = image.GetAlpha();
417#endif
418 unsigned char *data = image.GetData();
419
420 const auto half = settings.GetFFTLength() / 2;
421 const double binUnit = sampleRate / (2 * half);
422 const float *freq = 0;
423 const sampleCount *where = 0;
424 // Get the cache from the leader clip, but pass the WaveChannelInterval
425 // to use the correct channel in the cache
427 clip, freq, settings, where, (size_t)hiddenMid.width, t0,
428 averagePixelsPerSecond);
429 auto nBins = settings.NBins();
430
431 float minFreq, maxFreq;
433 .GetBounds(track, minFreq, maxFreq);
434
435 const SpectrogramSettings::ScaleType scaleType = settings.scaleType;
436
437 // nearest frequency to each pixel row from number scale, for selecting
438 // the desired fft bin(s) for display on that row
439 float *bins = (float*)alloca(sizeof(*bins)*(hiddenMid.height + 1));
440 {
441 const NumberScale numberScale( settings.GetScale( minFreq, maxFreq ) );
442
443 NumberScale::Iterator it = numberScale.begin(mid.height);
444 float nextBin = std::max( 0.0f, std::min( float(nBins - 1),
445 settings.findBin( *it, binUnit ) ) );
446
447 int yy;
448 for (yy = 0; yy < hiddenMid.height; ++yy) {
449 bins[yy] = nextBin;
450 nextBin = std::max( 0.0f, std::min( float(nBins - 1),
451 settings.findBin( *++it, binUnit ) ) );
452 }
453 bins[yy] = nextBin;
454 }
455
456#ifdef EXPERIMENTAL_FFT_Y_GRID
457 const float
458 log2 = logf(2.0f),
459 scale2 = (lmax - lmin) / log2,
460 lmin2 = lmin / log2;
461
462 ArrayOf<bool> yGrid{size_t(mid.height)};
463 for (int yy = 0; yy < mid.height; ++yy) {
464 float n = (float(yy) / mid.height*scale2 - lmin2) * 12;
465 float n2 = (float(yy + 1) / mid.height*scale2 - lmin2) * 12;
466 float f = float(minFreq) / (fftSkipPoints + 1)*powf(2.0f, n / 12.0f + lmin2);
467 float f2 = float(minFreq) / (fftSkipPoints + 1)*powf(2.0f, n2 / 12.0f + lmin2);
468 n = logf(f / 440) / log2 * 12;
469 n2 = logf(f2 / 440) / log2 * 12;
470 if (floor(n) < floor(n2))
471 yGrid[yy] = true;
472 else
473 yGrid[yy] = false;
474 }
475#endif //EXPERIMENTAL_FFT_Y_GRID
476
477 auto &clipCache = WaveClipSpectrumCache::Get(clip.GetClip());
478 auto &specPxCache = clipCache.mSpecPxCaches[clip.GetChannelIndex()];
479 if (!updated && specPxCache &&
480 ((int)specPxCache->len == hiddenMid.height * hiddenMid.width)
481 && scaleType == specPxCache->scaleType
482 && gain == specPxCache->gain
483 && range == specPxCache->range
484 && minFreq == specPxCache->minFreq
485 && maxFreq == specPxCache->maxFreq
486#ifdef EXPERIMENTAL_FFT_Y_GRID
487 && fftYGrid==fftYGridOld
488#endif //EXPERIMENTAL_FFT_Y_GRID
489#ifdef EXPERIMENTAL_FIND_NOTES
490 && fftFindNotes == artist->fftFindNotesOld
491 && findNotesMinA == artist->findNotesMinAOld
492 && numberOfMaxima == artist->findNotesNOld
493 && findNotesQuantize == artist->findNotesQuantizeOld
494#endif
495 ) {
496 // Wave clip's spectrum cache is up to date,
497 // and so is the spectrum pixel cache
498 }
499 else {
500 // Update the spectrum pixel cache
501 specPxCache = std::make_unique<SpecPxCache>(hiddenMid.width * hiddenMid.height);
502 specPxCache->scaleType = scaleType;
503 specPxCache->gain = gain;
504 specPxCache->range = range;
505 specPxCache->minFreq = minFreq;
506 specPxCache->maxFreq = maxFreq;
507#ifdef EXPERIMENTAL_FIND_NOTES
508 artist->fftFindNotesOld = fftFindNotes;
509 artist->findNotesMinAOld = findNotesMinA;
510 artist->findNotesNOld = numberOfMaxima;
511 artist->findNotesQuantizeOld = findNotesQuantize;
512#endif
513
514#ifdef EXPERIMENTAL_FIND_NOTES
515 float log2 = logf( 2.0f ),
516 lmin = logf( minFreq ), lmax = logf( maxFreq ), scale = lmax - lmin,
517 lmins = lmin,
518 lmaxs = lmax
519 ;
520#endif //EXPERIMENTAL_FIND_NOTES
521
522#ifdef EXPERIMENTAL_FIND_NOTES
523 int maxima[128];
524 float maxima0[128], maxima1[128];
525 const float
526 f2bin = half / (sampleRate / 2.0f),
527 bin2f = 1.0f / f2bin,
528 minDistance = powf(2.0f, 2.0f / 12.0f),
529 i0 = expf(lmin) / binUnit,
530 i1 = expf(scale + lmin) / binUnit,
531 minColor = 0.0f;
532 const size_t maxTableSize = 1024;
533 ArrayOf<int> indexes{ maxTableSize };
534#endif //EXPERIMENTAL_FIND_NOTES
535
536#ifdef _OPENMP
537#pragma omp parallel for
538#endif
539 for (int xx = 0; xx < hiddenMid.width; ++xx) {
540#ifdef EXPERIMENTAL_FIND_NOTES
541 int maximas = 0;
542 const int x0 = nBins * xx;
543 if (fftFindNotes) {
544 for (int i = maxTableSize - 1; i >= 0; i--)
545 indexes[i] = -1;
546
547 // Build a table of (most) values, put the index in it.
548 for (int i = (int)(i0); i < (int)(i1); i++) {
549 float freqi = freq[x0 + (int)(i)];
550 int value = (int)((freqi + gain + range) / range*(maxTableSize - 1));
551 if (value < 0)
552 value = 0;
553 if (value >= maxTableSize)
554 value = maxTableSize - 1;
555 indexes[value] = i;
556 }
557 // Build from the indices an array of maxima.
558 for (int i = maxTableSize - 1; i >= 0; i--) {
559 int index = indexes[i];
560 if (index >= 0) {
561 float freqi = freq[x0 + index];
562 if (freqi < findNotesMinA)
563 break;
564
565 bool ok = true;
566 for (int m = 0; m < maximas; m++) {
567 // Avoid to store very close maxima.
568 float maxm = maxima[m];
569 if (maxm / index < minDistance && index / maxm < minDistance) {
570 ok = false;
571 break;
572 }
573 }
574 if (ok) {
575 maxima[maximas++] = index;
576 if (maximas >= numberOfMaxima)
577 break;
578 }
579 }
580 }
581
582// The f2pix helper macro converts a frequency into a pixel coordinate.
583#define f2pix(f) (logf(f)-lmins)/(lmaxs-lmins)*hiddenMid.height
584
585 // Possibly quantize the maxima frequencies and create the pixel block limits.
586 for (int i = 0; i < maximas; i++) {
587 int index = maxima[i];
588 float f = float(index)*bin2f;
589 if (findNotesQuantize)
590 {
591 f = expf((int)(log(f / 440) / log2 * 12 - 0.5) / 12.0f*log2) * 440;
592 maxima[i] = f*f2bin;
593 }
594 float f0 = expf((log(f / 440) / log2 * 24 - 1) / 24.0f*log2) * 440;
595 maxima0[i] = f2pix(f0);
596 float f1 = expf((log(f / 440) / log2 * 24 + 1) / 24.0f*log2) * 440;
597 maxima1[i] = f2pix(f1);
598 }
599 }
600
601 int it = 0;
602 bool inMaximum = false;
603#endif //EXPERIMENTAL_FIND_NOTES
604
605 for (int yy = 0; yy < hiddenMid.height; ++yy) {
606 const float bin = bins[yy];
607 const float nextBin = bins[yy+1];
608
610 const float value = findValue
611 (freq + nBins * xx, bin, nextBin, nBins, autocorrelation, gain, range);
612 specPxCache->values[xx * hiddenMid.height + yy] = value;
613 }
614 else {
615 float value;
616
617#ifdef EXPERIMENTAL_FIND_NOTES
618 if (fftFindNotes) {
619 if (it < maximas) {
620 float i0 = maxima0[it];
621 if (yy >= i0)
622 inMaximum = true;
623
624 if (inMaximum) {
625 float i1 = maxima1[it];
626 if (yy + 1 <= i1) {
627 value = findValue(freq + x0, bin, nextBin, nBins, autocorrelation, gain, range);
628 if (value < findNotesMinA)
629 value = minColor;
630 }
631 else {
632 it++;
633 inMaximum = false;
634 value = minColor;
635 }
636 }
637 else {
638 value = minColor;
639 }
640 }
641 else
642 value = minColor;
643 }
644 else
645#endif //EXPERIMENTAL_FIND_NOTES
646 {
647 value = findValue
648 (freq + nBins * xx, bin, nextBin, nBins, autocorrelation, gain, range);
649 }
650 specPxCache->values[xx * hiddenMid.height + yy] = value;
651 } // logF
652 } // each yy
653 } // each xx
654 } // updating cache
655
656 float selBinLo = settings.findBin( freqLo, binUnit);
657 float selBinHi = settings.findBin( freqHi, binUnit);
658 float selBinCenter = (freqLo < 0 || freqHi < 0)
659 ? -1
660 : settings.findBin( sqrt(freqLo * freqHi), binUnit );
661
662 const bool isSpectral = settings.SpectralSelectionEnabled();
663 const bool hidden = (ZoomInfo::HIDDEN == zoomInfo.GetFisheyeState());
664 const int begin = hidden
665 ? 0
666 : std::max(0, (int)(zoomInfo.GetFisheyeLeftBoundary(-leftOffset)));
667 const int end = hidden
668 ? 0
669 : std::min(mid.width, (int)(zoomInfo.GetFisheyeRightBoundary(-leftOffset)));
670 const size_t numPixels = std::max(0, end - begin);
671
672 SpecCache specCache;
673
674 // need explicit resize since specCache.where[] accessed before Populate()
675 specCache.Grow(numPixels, settings, -1, t0);
676
677 if (numPixels > 0) {
678 for (int ii = begin; ii < end; ++ii) {
679 const double time = zoomInfo.PositionToTime(ii, -leftOffset) - playStartTime;
680 specCache.where[ii - begin] =
681 sampleCount(0.5 + sampleRate / stretchRatio * time);
682 }
683 specCache.Populate(
684 settings, clip, 0, 0, numPixels,
685 0 // FIXME: PRL -- make reassignment work with fisheye
686 );
687 }
688
689 // build color gradient tables (not thread safe)
692
693 // left pixel column of the fisheye
694 int fisheyeLeft = zoomInfo.GetFisheyeLeftBoundary(-leftOffset);
695
696 // Bug 2389 - always draw at least one pixel of selection.
697 int selectedX = zoomInfo.TimeToPosition(selectedRegion.t0(), -leftOffset);
698
699#ifdef _OPENMP
700#pragma omp parallel for
701#endif
702
703 const NumberScale numberScale(settings.GetScale(minFreq, maxFreq));
704 int windowSize = mpSpectralData->GetWindowSize();
705 int hopSize = mpSpectralData->GetHopSize();
706 double sr = mpSpectralData->GetSR();
707 auto &dataHistory = mpSpectralData->dataHistory;
708
709 // Lazy way to add all hops and bins required for rendering
710 dataHistory.push_back(mpSpectralData->dataBuffer);
711
712 // Generate combined hops and bins map for rendering
713 std::map<long long, std::set<int>> hopBinMap;
714 for(auto vecIter = dataHistory.begin(); vecIter != dataHistory.end(); ++vecIter){
715 for(const auto &hopMap: *vecIter){
716 for(const auto &binNum: hopMap.second)
717 hopBinMap[hopMap.first].insert(binNum);
718 }
719 }
720
721 // Lambda for converting yy (not mouse coord!) to respective freq. bins
722 auto yyToFreqBin = [&](int yy){
723 const double p = double(yy) / hiddenMid.height;
724 float convertedFreq = numberScale.PositionToValue(p);
725 float convertedFreqBinNum = convertedFreq / (sr / windowSize);
726
727 // By default lrintf will round to nearest by default, rounding to even on tie.
728 // std::round that was used here before rounds halfway cases away from zero.
729 // However, we can probably tolerate rounding issues here, as this will only slightly affect
730 // the visuals.
731 return static_cast<int>(lrintf(convertedFreqBinNum));
732 };
733
734 for (int xx = 0; xx < mid.width; ++xx) {
735 int correctedX = xx + leftOffset - hiddenLeftOffset;
736
737 // in fisheye mode the time scale has changed, so the row values aren't cached
738 // in the loop above, and must be fetched from fft cache
739 float* uncached;
740 if (!zoomInfo.InFisheye(xx, -leftOffset)) {
741 uncached = 0;
742 }
743 else {
744 int specIndex = (xx - fisheyeLeft) * nBins;
745 wxASSERT(specIndex >= 0 && specIndex < (int)specCache.freq.size());
746 uncached = &specCache.freq[specIndex];
747 }
748
749 // zoomInfo must be queried for each column since with fisheye enabled
750 // time between columns is variable
751 const auto w0 = sampleCount(
752 0.5 + sampleRate / stretchRatio *
753 (zoomInfo.PositionToTime(xx, -leftOffset) - playStartTime));
754
755 const auto w1 = sampleCount(
756 0.5 + sampleRate / stretchRatio *
757 (zoomInfo.PositionToTime(xx + 1, -leftOffset) - playStartTime));
758
759 bool maybeSelected = ssel0 <= w0 && w1 < ssel1;
760 maybeSelected = maybeSelected || (xx == selectedX);
761
762 // In case the xx matches the hop number, it will be used as iterator for frequency bins
763 std::set<int> *pSelectedBins = nullptr;
764 std::set<int>::iterator freqBinIter;
765 auto advanceFreqBinIter = [&](int nextBinRounded){
766 while (freqBinIter != pSelectedBins->end() &&
767 *freqBinIter < nextBinRounded)
768 ++freqBinIter;
769 };
770
771 bool hitHopNum = false;
772 if (onBrushTool) {
773 int convertedHopNum = (w0.as_long_long() + hopSize / 2) / hopSize;
774 hitHopNum = (hopBinMap.find(convertedHopNum) != hopBinMap.end());
775 if(hitHopNum) {
776 pSelectedBins = &hopBinMap[convertedHopNum];
777 freqBinIter = pSelectedBins->begin();
778 advanceFreqBinIter(yyToFreqBin(0));
779 }
780 }
781
782 for (int yy = 0; yy < hiddenMid.height; ++yy) {
783 if(onBrushTool)
784 maybeSelected = false;
785 const float bin = bins[yy];
786 const float nextBin = bins[yy+1];
787 auto binRounded = yyToFreqBin(yy);
788 auto nextBinRounded = yyToFreqBin(yy + 1);
789
790 if(hitHopNum
791 && freqBinIter != pSelectedBins->end()
792 && binRounded == *freqBinIter)
793 maybeSelected = true;
794
795 if (hitHopNum)
796 advanceFreqBinIter(nextBinRounded);
797
798 // For spectral selection, determine what colour
799 // set to use. We use a darker selection if
800 // in both spectral range and time range.
801
803
804 // If we are in the time selected range, then we may use a different color set.
805 if (maybeSelected) {
806 selected =
807 ChooseColorSet(bin, nextBin, selBinLo, selBinCenter, selBinHi,
808 (xx + leftOffset - hiddenLeftOffset) / DASH_LENGTH, isSpectral);
809 if ( onBrushTool && selected != AColor::ColorGradientUnselected )
810 // use only two sets of colors
812 }
813
814 const float value = uncached
815 ? findValue(uncached, bin, nextBin, nBins, autocorrelation, gain, range)
816 : specPxCache->values[correctedX * hiddenMid.height + yy];
817
818 unsigned char rv, gv, bv;
819 GetColorGradient(value, selected, colorScheme, &rv, &gv, &bv);
820
821#ifdef EXPERIMENTAL_FFT_Y_GRID
822 if (fftYGrid && yGrid[yy]) {
823 rv /= 1.1f;
824 gv /= 1.1f;
825 bv /= 1.1f;
826 }
827#endif //EXPERIMENTAL_FFT_Y_GRID
828 int px = ((mid.height - 1 - yy) * mid.width + xx);
829#ifdef EXPERIMENTAL_SPECTROGRAM_OVERLAY
830 // More transparent the closer to zero intensity.
831 alpha[px]= wxMin( 200, (value+0.3) * 500) ;
832#endif
833 px *=3;
834 data[px++] = rv;
835 data[px++] = gv;
836 data[px] = bv;
837 } // each yy
838 } // each xx
839
840 dataHistory.pop_back();
841 wxBitmap converted = wxBitmap(image);
842
843 wxMemoryDC memDC;
844
845 memDC.SelectObject(converted);
846
847 dc.Blit(mid.x, mid.y, mid.width, mid.height, &memDC, 0, 0, wxCOPY, FALSE);
848
849 // Draw clip edges, as also in waveform view, which improves the appearance
850 // of split views
851 {
852 auto clipRect = ClipParameters::GetClipRect(clip, zoomInfo, rect);
853 TrackArt::DrawClipEdges(dc, clipRect, selected);
854 }
855}
856}
857
858void SpectrumView::DoDraw(TrackPanelDrawingContext& context, size_t channel,
859 const WaveTrack &track, const WaveTrack::Interval* selectedClip,
860 const wxRect & rect)
861{
862 const auto artist = TrackArtist::Get( context );
863 const auto &blankSelectedBrush = artist->blankSelectedBrush;
864 const auto &blankBrush = artist->blankBrush;
866 context, rect, &track, blankSelectedBrush, blankBrush );
867
868 // Really useful channel numbers are not yet passed in
869 // TODO wide wave tracks -- really use channel
870 assert(channel == 0);
871 channel = (track.IsLeader() ? 0 : 1);
872
873 // TODO wide wave tracks -- remove this workaround
874 auto pLeader = *track.GetHolder()->Find(&track);
875 assert(pLeader->IsLeader());
876
877 for (const auto pInterval : static_cast<const WaveTrack*>(pLeader)
878 ->GetChannel(channel)->Intervals()
879 ) {
880 bool selected = selectedClip &&
881 WaveChannelView::WideClipContains(*selectedClip, pInterval->GetClip());
882 DrawClipSpectrum(context, track, *pInterval, rect, mpSpectralData,
883 selected);
884 }
885
886 DrawBoldBoundaries(context, track, rect);
887}
888
890 TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass )
891{
892 if ( iPass == TrackArtist::PassTracks ) {
893 auto &dc = context.dc;
894
895 const auto wt = std::static_pointer_cast<const WaveTrack>(
896 FindTrack()->SubstitutePendingChangedTrack());
897
898 const auto artist = TrackArtist::Get( context );
899
900#if defined(__WXMAC__)
901 wxAntialiasMode aamode = dc.GetGraphicsContext()->GetAntialiasMode();
902 dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE);
903#endif
904
905 auto waveChannelView = GetWaveChannelView().lock();
906 wxASSERT(waveChannelView.use_count());
907
908 auto selectedClip = waveChannelView->GetSelectedClip();
909 DoDraw(context, GetChannelIndex(), *wt, selectedClip.get(), rect);
910
911#if defined(__WXMAC__)
912 dc.GetGraphicsContext()->SetAntialiasMode(aamode);
913#endif
914 }
915 WaveChannelSubView::Draw(context, rect, iPass);
916}
917
919 [](WaveChannelView &view){
920 return std::make_shared<SpectrumView>(view);
921 }
922};
923
924// The following attaches the spectrogram settings item to the wave track popup
925// menu. It is appropriate only to spectrum view and so is kept in this
926// source file with the rest of the spectrum view implementation.
927#include "WaveTrackControls.h"
928#include "AudioIOBase.h"
929#include "../../../../MenuCreator.h"
930#include "ProjectHistory.h"
931#include "../../../../RefreshCode.h"
932#include "../../../../prefs/PrefsDialog.h"
933#include "../../../../prefs/SpectrumPrefs.h"
934#include "AudacityMessageBox.h"
935#include "../../../../widgets/PopupMenuTable.h"
936
937namespace {
939
942 {
943 static SpectrogramSettingsHandler instance;
944 return instance;
945 }
946
947 void OnSpectrogramSettings(wxCommandEvent &);
948
949 void InitUserData(void *pUserData) override
950 {
951 mpData = static_cast< PlayableTrackControls::InitMenuData* >(pUserData);
952 }
953};
954
955void SpectrogramSettingsHandler::OnSpectrogramSettings(wxCommandEvent &)
956{
957 class ViewSettingsDialog final : public PrefsDialog
958 {
959 public:
960 ViewSettingsDialog(wxWindow *parent, AudacityProject &project,
962 int page)
963 : PrefsDialog(parent, &project, title, factories)
964 , mPage(page)
965 {
966 }
967
968 long GetPreferredPage() override
969 {
970 return mPage;
971 }
972
973 void SavePreferredPage() override
974 {
975 }
976
977 private:
978 const int mPage;
979 };
980
981 auto gAudioIO = AudioIOBase::Get();
982 if (gAudioIO->IsBusy()){
984 XO(
985"To change Spectrogram Settings, stop any\n playing or recording first."),
986 XO("Stop the Audio First"),
987 wxOK | wxICON_EXCLAMATION | wxCENTRE);
988 return;
989 }
990
991 WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
992
993 PrefsPanel::Factories factories;
994 // factories.push_back(WaveformPrefsFactory( pTrack ));
995 factories.push_back(SpectrumPrefsFactory( pTrack ));
996 const int page =
997 // (pTrack->GetDisplay() == WaveChannelViewConstants::Spectrum) ? 1 :
998 0;
999
1000 auto title = XO("%s:").Format( pTrack->GetName() );
1001 ViewSettingsDialog dialog(
1002 mpData->pParent, mpData->project, title, factories, page);
1003
1004 if (0 != dialog.ShowModal()) {
1005 // Redraw
1006 AudacityProject *const project = &mpData->project;
1007 ProjectHistory::Get( *project ).ModifyState(true);
1008 //Bug 1725 Toolbar was left greyed out.
1009 //This solution is overkill, but does fix the problem and is what the
1010 //prefs dialog normally does.
1012 mpData->result = RefreshCode::RefreshAll;
1013 }
1014}
1015
1018 { "SubViews/Extra" },
1019 std::make_unique<PopupMenuSection>( "SpectrogramSettings",
1020 // Conditionally add menu item for settings, if showing spectrum
1021 PopupMenuTable::Adapt< WaveTrackPopupMenuTable >(
1022 [](WaveTrackPopupMenuTable &table)
1023 {
1024 using Entry = PopupMenuTable::Entry;
1025 static const int OnSpectrogramSettingsID =
1027
1028 const auto pTrack = &table.FindWaveTrack();
1029 const auto &view = WaveChannelView::Get(*pTrack);
1030 const auto displays = view.GetDisplays();
1031 bool hasSpectrum = (displays.end() != std::find(
1032 displays.begin(), displays.end(),
1034 WaveChannelViewConstants::Spectrum, {} }
1035 ) );
1036 return hasSpectrum
1037 // In future, we might move this to the context menu of the
1038 // Spectrum vertical ruler.
1039 // (But the latter won't be satisfactory without a means to
1040 // open that other context menu with keystrokes only, and that
1041 // would require some notion of a focused sub-view.)
1042 ? std::make_unique<Entry>("SpectrogramSettings",
1043 Entry::Item,
1044 OnSpectrogramSettingsID,
1045 XXO("S&pectrogram Settings..."),
1046 (wxCommandEventFunction)
1047 (&SpectrogramSettingsHandler::OnSpectrogramSettings),
1048 SpectrogramSettingsHandler::Instance(),
1049 []( PopupMenuHandler &handler, wxMenu &menu, int id ){
1050 // Bug 1253. Shouldn't open preferences if audio is busy.
1051 // We can't change them on the fly yet anyway.
1052 auto gAudioIO = AudioIOBase::Get();
1053 menu.Enable(id, !gAudioIO->IsBusy());
1054 } )
1055 : nullptr;
1056 } ) )
1057};
1058}
1059
1060static bool ShouldCaptureEvent(wxKeyEvent& event, SpectralData *pData)
1061{
1062 const auto keyCode = event.GetKeyCode();
1063 return
1064 (keyCode == WXK_BACK || keyCode == WXK_DELETE ||
1065 keyCode == WXK_NUMPAD_DELETE)
1066 && pData && !pData->dataHistory.empty();
1067}
1068
1070 wxKeyEvent& event, ViewInfo&, wxWindow*, AudacityProject*)
1071{
1072 bool capture = ShouldCaptureEvent(event, mpSpectralData.get());
1073 event.Skip(!capture);
1075}
1076
1077unsigned SpectrumView::KeyDown(wxKeyEvent& event, ViewInfo& viewInfo, wxWindow*, AudacityProject* project)
1078{
1079 bool capture = ShouldCaptureEvent(event, mpSpectralData.get());
1080 event.Skip(!capture);
1082 // Not RefreshCell, because there might be effects in multiple tracks
1085}
1086
1088 wxKeyEvent &event, ViewInfo&, wxWindow*, AudacityProject* )
1089{
1090 bool capture = ShouldCaptureEvent(event, mpSpectralData.get());
1091 event.Skip(!capture);
1093}
1094
1095#ifdef EXPERIMENTAL_SPECTRAL_EDITING
1096// Attach some related menu items
1097#include "../../../ui/SelectHandle.h"
1098#include "../../../../CommonCommandFlags.h"
1099#include "Project.h"
1100#include "../../../../SpectrumAnalyst.h"
1101#include "CommandContext.h"
1102
1103namespace {
1104void DoNextPeakFrequency(AudacityProject &project, bool up)
1105{
1106 auto &tracks = TrackList::Get( project );
1107 auto &viewInfo = ViewInfo::Get( project );
1108
1109 // Find the first selected wave track that is in a spectrogram view.
1110 const WaveTrack *pTrack {};
1111 for (auto wt : tracks.Selected<const WaveTrack>()) {
1112 const auto displays = WaveChannelView::Get(*wt).GetDisplays();
1113 bool hasSpectrum = (displays.end() != std::find(
1114 displays.begin(), displays.end(),
1116 WaveChannelViewConstants::Spectrum, {} }
1117 ) );
1118 if (hasSpectrum) {
1119 pTrack = wt;
1120 break;
1121 }
1122 }
1123
1124 if (pTrack) {
1125 SpectrumAnalyst analyst;
1126 SelectHandle::SnapCenterOnce(analyst, viewInfo, pTrack, up);
1128 }
1129}
1130
1131struct Handler : CommandHandlerObject, ClientData::Base {
1132
1133// Handler state:
1134double mLastF0{ SelectedRegion::UndefinedFrequency };
1135double mLastF1{ SelectedRegion::UndefinedFrequency };
1136
1137void OnToggleSpectralSelection(const CommandContext &context)
1138{
1139 auto &project = context.project;
1140 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
1141
1142 const double f0 = selectedRegion.f0();
1143 const double f1 = selectedRegion.f1();
1144 const bool haveSpectralSelection =
1147 if (haveSpectralSelection)
1148 {
1149 mLastF0 = f0;
1150 mLastF1 = f1;
1151 selectedRegion.setFrequencies
1153 }
1154 else
1155 selectedRegion.setFrequencies(mLastF0, mLastF1);
1156
1158}
1159
1160void OnNextHigherPeakFrequency(const CommandContext &context)
1161{
1162 auto &project = context.project;
1163 DoNextPeakFrequency(project, true);
1164}
1165
1166void OnNextLowerPeakFrequency(const CommandContext &context)
1167{
1168 auto &project = context.project;
1169 DoNextPeakFrequency(project, false);
1170}
1171};
1172
1173// Handler is stateful. Needs a factory registered with
1174// AudacityProject.
1176 [](AudacityProject&) {
1177 return std::make_unique< Handler >(); } };
1178
1180 return project.AttachedObjects::Get< Handler >( key );
1181};
1182
1183using namespace MenuRegistry;
1184#define FN(X) (& Handler :: X)
1185
1186auto SpectralSelectionMenu()
1187{
1188 static auto menu = std::shared_ptr{
1190 Menu( wxT("Spectral"), XXO("S&pectral"),
1191 Command( wxT("ToggleSpectralSelection"),
1192 XXO("To&ggle Spectral Selection"), FN(OnToggleSpectralSelection),
1193 TracksExistFlag(), wxT("Q") ),
1194 Command( wxT("NextHigherPeakFrequency"),
1195 XXO("Next &Higher Peak Frequency"), FN(OnNextHigherPeakFrequency),
1196 TracksExistFlag() ),
1197 Command( wxT("NextLowerPeakFrequency"),
1198 XXO("Next &Lower Peak Frequency"), FN(OnNextLowerPeakFrequency),
1199 TracksExistFlag() )
1200 ) ) };
1201 return menu;
1202}
1203
1204#undef FN
1205
1206AttachedItem sAttachment2{ Indirect(SpectralSelectionMenu()),
1207 Placement{ wxT("Select/Basic"), { OrderingHint::After, wxT("Region") } }
1208};
1209
1210}
1211#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
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
PrefsPanel::Factory SpectrumPrefsFactory(WaveTrack *wt)
static WaveChannelSubViewType::RegisteredType reg
static const WaveChannelSubViews::RegisteredFactory key
static WaveChannelSubView::Type sType
static bool ShouldCaptureEvent(wxKeyEvent &event, SpectralData *pData)
static UIHandlePtr BrushHandleHitTest(std::weak_ptr< BrushHandle > &holder, const TrackPanelMouseState &st, const AudacityProject *pProject, const std::shared_ptr< SpectrumView > &pChannelView, const std::shared_ptr< SpectralData > &mpData)
const auto tracks
const auto project
static Settings & settings()
Definition: TrackInfo.cpp:69
std::shared_ptr< Subclass > AssignUIHandlePtr(std::weak_ptr< Subclass > &holder, const std::shared_ptr< Subclass > &pNew)
Definition: UIHandle.h:159
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:94
static ChannelView & Get(Channel &channel)
virtual void DoSetMinimized(bool isMinimized)
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:274
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
AudacityProject & project
size_t GetChannelIndex() const
std::shared_ptr< Track > FindTrack()
static void RebuildAllMenuBars()
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)
Generates classes whose instances register items at construction.
Definition: Registry.h:388
static void SnapCenterOnce(SpectrumAnalyst &analyst, ViewInfo &viewInfo, const WaveTrack *pTrack, bool up)
Defines a selected portion of a project.
double t1() const
double t0() const
static const int UndefinedFrequency
void Grow(size_t len_, SpectrogramSettings &settings, double samplesPerPixel, double start)
void Populate(const SpectrogramSettings &settings, const WaveChannelInterval &clip, int copyBegin, int copyEnd, size_t numPixels, double pixelsPerSecond)
std::vector< float > freq
Definition: SpectrumCache.h:67
std::vector< sampleCount > where
Definition: SpectrumCache.h:68
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
bool IsSpectral() const override
void DoDraw(TrackPanelDrawingContext &context, size_t channel, const WaveTrack &track, const WaveTrack::Interval *selectedClip, const wxRect &rect)
std::shared_ptr< SpectralData > mpSpectralData
Definition: SpectrumView.h:148
std::weak_ptr< BrushHandle > mBrushHandle
Definition: SpectrumView.h:144
unsigned KeyDown(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent, AudacityProject *project) override
std::shared_ptr< SpectralData > mpBackupSpectralData
Definition: SpectrumView.h:148
const Type & SubViewType() const override
unsigned Char(wxKeyEvent &event, ViewInfo &viewInfo, wxWindow *pParent, AudacityProject *project) override
std::shared_ptr< SpectralData > GetSpectralData()
SpectrumView(WaveChannelView &waveChannelView, const SpectrumView &src)=delete
std::shared_ptr< ChannelVRulerControls > DoGetVRulerControls() override
std::vector< UIHandlePtr > DetailedHitTest(const TrackPanelMouseState &state, const AudacityProject *pProject, int currentTool, bool bMultiTool) override
void CopyToSubView(WaveChannelSubView *destSubView) const override
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:69
bool GetSelected() const
Selectedness is always the same for all channels of a group.
Definition: Track.cpp:70
TrackList * GetHolder() const
Definition: Track.h:1480
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:56
TrackIter< Track > Find(Track *pTrack)
Definition: Track.cpp:519
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
virtual void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass)
Holds a msgid for the translation catalog; may also bind format arguments.
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:215
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
double GetStretchRatio() const override
Definition: WaveTrack.cpp:128
double GetPlayStartTime() const override
Definition: WaveTrack.cpp:113
sampleCount GetVisibleSampleCount() const override
Definition: WaveTrack.cpp:103
int GetRate() const override
Definition: WaveTrack.cpp:108
const WaveClip & GetClip() const
Definition: WaveTrack.h:79
size_t GetChannelIndex() const
Definition: WaveTrack.h:81
sampleCount TimeToSamples(double time) const override
Definition: WaveTrack.cpp:123
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< WaveChannelView > GetWaveChannelView() const
static void DrawBoldBoundaries(TrackPanelDrawingContext &context, const WaveTrack &track, const wxRect &rect)
static bool ClipDetailsVisible(const ClipTimes &clip, const ZoomInfo &zoomInfo, const wxRect &viewRect)
static WaveChannelView & Get(WaveChannel &channel)
static bool WideClipContains(const WaveTrack::Interval &wideClip, const WaveClip &clip)
std::vector< WaveChannelSubView::Type > GetDisplays() const
std::shared_ptr< const WaveClip > GetClip(size_t iChannel) const
Definition: WaveTrack.h:1009
A Track that contains audio waveform data.
Definition: WaveTrack.h:227
bool IsLeader() const override
Definition: WaveTrack.cpp:2820
@ HIDDEN
Definition: ZoomInfo.h:156
virtual bool Read(const wxString &key, bool *value) const =0
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
#define lrintf(flt)
Definition: float_cast.h:170
constexpr auto Command
Definition: MenuRegistry.h:456
constexpr auto Menu
Items will appear in a main toolbar menu or in a sub-menu.
Definition: MenuRegistry.h:445
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< detail::IndirectItem< Item > > Indirect(const std::shared_ptr< Item > &ptr)
A convenience function.
Definition: Registry.h:175
AUDACITY_DLL_API void DrawClipFolded(wxDC &dc, const wxRect &rect)
Definition: TrackArt.cpp:346
AUDACITY_DLL_API void DrawClipEdges(wxDC &dc, const wxRect &clipRect, bool selected=false)
Definition: TrackArt.cpp:309
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:651
PopupMenuTable::AttachedItem sAttachment
AColor::ColorGradientChoice ChooseColorSet(float bin0, float bin1, float selBinLo, float selBinCenter, float selBinHi, int dashCount, bool isSpectral)
void DrawClipSpectrum(TrackPanelDrawingContext &context, const WaveTrack &track, const WaveChannelInterval &clip, const wxRect &rect, const std::shared_ptr< SpectralData > &mpSpectralData, bool selected)
static float findValue(const float *spectrum, float bin0, float bin1, unsigned nBins, bool autocorrelation, int gain, int range)
std::pair< sampleCount, sampleCount > GetSelectedSampleIndices(const SelectedRegion &selectedRegion, const WaveChannelInterval &clip, bool trackIsSelected)
__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 ClipTimes &clip, const ZoomInfo &zoomInfo, const wxRect &viewRect, bool *outShowSamples=nullptr)
static WaveClipSpectrumCache & Get(const WaveClip &clip)
bool GetSpectrogram(const WaveChannelInterval &clip, const float *&spectrogram, SpectrogramSettings &spectrogramSettings, 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.