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