Audacity  3.0.3
SpectrumView.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3 Audacity: A Digital Audio Editor
4 
5 SpectrumView.cpp
6 
7 Paul Licameli split from WaveTrackView.cpp
8 
9 **********************************************************************/
10 
11 
12 #include "SpectrumView.h"
13 
14 #include "SpectrumVRulerControls.h"
15 #include "WaveTrackView.h"
16 #include "WaveTrackViewConstants.h"
17 
18 #include "../../../../AColor.h"
19 #include "Prefs.h"
20 #include "../../../../NumberScale.h"
21 #include "../../../../TrackArtist.h"
22 #include "../../../../TrackPanelDrawingContext.h"
23 #include "../../../../ViewInfo.h"
24 #include "../../../../WaveClip.h"
25 #include "../../../../WaveTrack.h"
26 #include "../../../../prefs/SpectrogramSettings.h"
27 
28 #include <wx/dcmemory.h>
29 #include <wx/graphics.h>
30 
33  { wxT("Spectrogram"), XXO("&Spectrogram") }
34 };
35 
37 
38 SpectrumView::~SpectrumView() = default;
39 
41 {
42  return true;
43 }
44 
45 std::vector<UIHandlePtr> SpectrumView::DetailedHitTest(
46  const TrackPanelMouseState &state,
47  const AudacityProject *pProject, int currentTool, bool bMultiTool )
48 {
49  const auto wt = std::static_pointer_cast< WaveTrack >( FindTrack() );
50 
52  state, pProject, currentTool, bMultiTool, wt
53  ).second;
54 }
55 
56 void SpectrumView::DoSetMinimized( bool minimized )
57 {
58  auto wt = static_cast<WaveTrack*>( FindTrack().get() );
59 
60 #ifdef EXPERIMENTAL_HALF_WAVE
61  bool bHalfWave;
62  gPrefs->Read(wxT("/GUI/CollapseToHalfWave"), &bHalfWave, false);
63  if( bHalfWave && minimized)
64  {
65  // It is all right to set the top of scale to a huge number,
66  // not knowing the track rate here -- because when retrieving the
67  // value, then we pass in a sample rate and clamp it above to the
68  // Nyquist frequency.
69  constexpr auto max = std::numeric_limits<float>::max();
70  const bool spectrumLinear =
71  (wt->GetSpectrogramSettings().scaleType ==
73  // Zoom out full
74  wt->SetSpectrumBounds( spectrumLinear ? 0.0f : 1.0f, max );
75  }
76 #endif
77 
78  TrackView::DoSetMinimized( minimized );
79 }
80 
81 auto SpectrumView::SubViewType() const -> const Type &
82 {
83  return sType;
84 }
85 
86 std::shared_ptr<TrackVRulerControls> SpectrumView::DoGetVRulerControls()
87 {
88  return std::make_shared<SpectrumVRulerControls>( shared_from_this() );
89 }
90 
91 namespace
92 {
93 
94 static inline float findValue
95 (const float *spectrum, float bin0, float bin1, unsigned nBins,
96  bool autocorrelation, int gain, int range)
97 {
98  float value;
99 
100 
101 #if 0
102  // Averaging method
103  if ((int)(bin1) == (int)(bin0)) {
104  value = spectrum[(int)(bin0)];
105  } else {
106  float binwidth= bin1 - bin0;
107  value = spectrum[(int)(bin0)] * (1.f - bin0 + (int)bin0);
108 
109  bin0 = 1 + (int)(bin0);
110  while (bin0 < (int)(bin1)) {
111  value += spectrum[(int)(bin0)];
112  bin0 += 1.0;
113  }
114  // Do not reference past end of freq array.
115  if ((int)(bin1) >= (int)nBins) {
116  bin1 -= 1.0;
117  }
118 
119  value += spectrum[(int)(bin1)] * (bin1 - (int)(bin1));
120  value /= binwidth;
121  }
122 #else
123  // Maximum method, and no apportionment of any single bins over multiple pixel rows
124  // See Bug971
125  int index, limitIndex;
126  if (autocorrelation) {
127  // bin = 2 * nBins / (nBins - 1 - array_index);
128  // Solve for index
129  index = std::max(0.0f, std::min(float(nBins - 1),
130  (nBins - 1) - (2 * nBins) / (std::max(1.0f, bin0))
131  ));
132  limitIndex = std::max(0.0f, std::min(float(nBins - 1),
133  (nBins - 1) - (2 * nBins) / (std::max(1.0f, bin1))
134  ));
135  }
136  else {
137  index = std::min<int>(nBins - 1, (int)(floor(0.5 + bin0)));
138  limitIndex = std::min<int>(nBins, (int)(floor(0.5 + bin1)));
139  }
140  value = spectrum[index];
141  while (++index < limitIndex)
142  value = std::max(value, spectrum[index]);
143 #endif
144  if (!autocorrelation) {
145  // Last step converts dB to a 0.0-1.0 range
146  value = (value + range + gain) / (double)range;
147  }
148  value = std::min(1.0f, std::max(0.0f, value));
149  return value;
150 }
151 
152 // dashCount counts both dashes and the spaces between them.
154 ChooseColorSet( float bin0, float bin1, float selBinLo,
155  float selBinCenter, float selBinHi, int dashCount, bool isSpectral )
156 {
157  if (!isSpectral)
159  if ((selBinCenter >= 0) && (bin0 <= selBinCenter) &&
160  (selBinCenter < bin1))
162  if ((0 == dashCount % 2) &&
163  (((selBinLo >= 0) && (bin0 <= selBinLo) && ( selBinLo < bin1)) ||
164  ((selBinHi >= 0) && (bin0 <= selBinHi) && ( selBinHi < bin1))))
166  if ((selBinLo < 0 || selBinLo < bin1) && (selBinHi < 0 || selBinHi > bin0))
168 
170 }
171 
173  WaveTrackCache &waveTrackCache,
174  const WaveClip *clip,
175  const wxRect & rect,
176  bool selected)
177 {
178  auto &dc = context.dc;
179  const auto artist = TrackArtist::Get( context );
180  const auto &selectedRegion = *artist->pSelectedRegion;
181  const auto &zoomInfo = *artist->pZoomInfo;
182 
183 #ifdef PROFILE_WAVEFORM
184  Profiler profiler;
185 #endif
186 
187  const WaveTrack *const track = waveTrackCache.GetTrack().get();
189  const bool autocorrelation = (settings.algorithm == SpectrogramSettings::algPitchEAC);
190 
191  enum { DASH_LENGTH = 10 /* pixels */ };
192 
193  const ClipParameters params{
194  true, track, clip, rect, selectedRegion, zoomInfo };
195  const wxRect &hiddenMid = params.hiddenMid;
196  // The "hiddenMid" rect contains the part of the display actually
197  // containing the waveform, as it appears without the fisheye. If it's empty, we're done.
198  if (hiddenMid.width <= 0) {
199  return;
200  }
201 
202  const double &t0 = params.t0;
203  const double &tOffset = params.tOffset;
204  const auto &ssel0 = params.ssel0;
205  const auto &ssel1 = params.ssel1;
206  const double &averagePixelsPerSample = params.averagePixelsPerSample;
207  const double &rate = params.rate;
208  const double &hiddenLeftOffset = params.hiddenLeftOffset;
209  const double &leftOffset = params.leftOffset;
210  const wxRect &mid = params.mid;
211 
212  double freqLo = SelectedRegion::UndefinedFrequency;
213  double freqHi = SelectedRegion::UndefinedFrequency;
214 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
215  freqLo = selectedRegion.f0();
216  freqHi = selectedRegion.f1();
217 #endif
218 
219  const int &colorScheme = settings.colorScheme;
220  const int &range = settings.range;
221  const int &gain = settings.gain;
222 
223 #ifdef EXPERIMENTAL_FIND_NOTES
224  const bool &fftFindNotes = settings.fftFindNotes;
225  const double &findNotesMinA = settings.findNotesMinA;
226  const int &numberOfMaxima = settings.numberOfMaxima;
227  const bool &findNotesQuantize = settings.findNotesQuantize;
228 #endif
229 #ifdef EXPERIMENTAL_FFT_Y_GRID
230  const bool &fftYGrid = settings.fftYGrid;
231 #endif
232 
233  dc.SetPen(*wxTRANSPARENT_PEN);
234 
235  // We draw directly to a bit image in memory,
236  // and then paint this directly to our offscreen
237  // bitmap. Note that this could be optimized even
238  // more, but for now this is not bad. -dmazzoni
239  wxImage image((int)mid.width, (int)mid.height);
240  if (!image.IsOk())
241  return;
242 #ifdef EXPERIMENTAL_SPECTROGRAM_OVERLAY
243  image.SetAlpha();
244  unsigned char *alpha = image.GetAlpha();
245 #endif
246  unsigned char *data = image.GetData();
247 
248  const auto half = settings.GetFFTLength() / 2;
249  const double binUnit = rate / (2 * half);
250  const float *freq = 0;
251  const sampleCount *where = 0;
252  bool updated;
253  {
254  const double pps = averagePixelsPerSample * rate;
255  updated = clip->GetSpectrogram(waveTrackCache, freq, where,
256  (size_t)hiddenMid.width,
257  t0, pps);
258  }
259  auto nBins = settings.NBins();
260 
261  float minFreq, maxFreq;
262  track->GetSpectrumBounds(&minFreq, &maxFreq);
263 
264  const SpectrogramSettings::ScaleType scaleType = settings.scaleType;
265 
266  // nearest frequency to each pixel row from number scale, for selecting
267  // the desired fft bin(s) for display on that row
268  float *bins = (float*)alloca(sizeof(*bins)*(hiddenMid.height + 1));
269  {
270  const NumberScale numberScale( settings.GetScale( minFreq, maxFreq ) );
271 
272  NumberScale::Iterator it = numberScale.begin(mid.height);
273  float nextBin = std::max( 0.0f, std::min( float(nBins - 1),
274  settings.findBin( *it, binUnit ) ) );
275 
276  int yy;
277  for (yy = 0; yy < hiddenMid.height; ++yy) {
278  bins[yy] = nextBin;
279  nextBin = std::max( 0.0f, std::min( float(nBins - 1),
280  settings.findBin( *++it, binUnit ) ) );
281  }
282  bins[yy] = nextBin;
283  }
284 
285 #ifdef EXPERIMENTAL_FFT_Y_GRID
286  const float
287  log2 = logf(2.0f),
288  scale2 = (lmax - lmin) / log2,
289  lmin2 = lmin / log2;
290 
291  ArrayOf<bool> yGrid{size_t(mid.height)};
292  for (int yy = 0; yy < mid.height; ++yy) {
293  float n = (float(yy) / mid.height*scale2 - lmin2) * 12;
294  float n2 = (float(yy + 1) / mid.height*scale2 - lmin2) * 12;
295  float f = float(minFreq) / (fftSkipPoints + 1)*powf(2.0f, n / 12.0f + lmin2);
296  float f2 = float(minFreq) / (fftSkipPoints + 1)*powf(2.0f, n2 / 12.0f + lmin2);
297  n = logf(f / 440) / log2 * 12;
298  n2 = logf(f2 / 440) / log2 * 12;
299  if (floor(n) < floor(n2))
300  yGrid[yy] = true;
301  else
302  yGrid[yy] = false;
303  }
304 #endif //EXPERIMENTAL_FFT_Y_GRID
305 
306  if (!updated && clip->mSpecPxCache->valid &&
307  ((int)clip->mSpecPxCache->len == hiddenMid.height * hiddenMid.width)
308  && scaleType == clip->mSpecPxCache->scaleType
309  && gain == clip->mSpecPxCache->gain
310  && range == clip->mSpecPxCache->range
311  && minFreq == clip->mSpecPxCache->minFreq
312  && maxFreq == clip->mSpecPxCache->maxFreq
313 #ifdef EXPERIMENTAL_FFT_Y_GRID
314  && fftYGrid==fftYGridOld
315 #endif //EXPERIMENTAL_FFT_Y_GRID
316 #ifdef EXPERIMENTAL_FIND_NOTES
317  && fftFindNotes == artist->fftFindNotesOld
318  && findNotesMinA == artist->findNotesMinAOld
319  && numberOfMaxima == artist->findNotesNOld
320  && findNotesQuantize == artist->findNotesQuantizeOld
321 #endif
322  ) {
323  // Wave clip's spectrum cache is up to date,
324  // and so is the spectrum pixel cache
325  }
326  else {
327  // Update the spectrum pixel cache
328  clip->mSpecPxCache = std::make_unique<SpecPxCache>(hiddenMid.width * hiddenMid.height);
329  clip->mSpecPxCache->valid = true;
330  clip->mSpecPxCache->scaleType = scaleType;
331  clip->mSpecPxCache->gain = gain;
332  clip->mSpecPxCache->range = range;
333  clip->mSpecPxCache->minFreq = minFreq;
334  clip->mSpecPxCache->maxFreq = maxFreq;
335 #ifdef EXPERIMENTAL_FIND_NOTES
336  artist->fftFindNotesOld = fftFindNotes;
337  artist->findNotesMinAOld = findNotesMinA;
338  artist->findNotesNOld = numberOfMaxima;
339  artist->findNotesQuantizeOld = findNotesQuantize;
340 #endif
341 
342 #ifdef EXPERIMENTAL_FIND_NOTES
343  float log2 = logf( 2.0f ),
344  lmin = logf( minFreq ), lmax = logf( maxFreq ), scale = lmax - lmin,
345  lmins = lmin,
346  lmaxs = lmax
347  ;
348 #endif //EXPERIMENTAL_FIND_NOTES
349 
350 #ifdef EXPERIMENTAL_FIND_NOTES
351  int maxima[128];
352  float maxima0[128], maxima1[128];
353  const float
354  f2bin = half / (rate / 2.0f),
355  bin2f = 1.0f / f2bin,
356  minDistance = powf(2.0f, 2.0f / 12.0f),
357  i0 = expf(lmin) / binUnit,
358  i1 = expf(scale + lmin) / binUnit,
359  minColor = 0.0f;
360  const size_t maxTableSize = 1024;
361  ArrayOf<int> indexes{ maxTableSize };
362 #endif //EXPERIMENTAL_FIND_NOTES
363 
364 #ifdef _OPENMP
365 #pragma omp parallel for
366 #endif
367  for (int xx = 0; xx < hiddenMid.width; ++xx) {
368 #ifdef EXPERIMENTAL_FIND_NOTES
369  int maximas = 0;
370  const int x0 = nBins * xx;
371  if (fftFindNotes) {
372  for (int i = maxTableSize - 1; i >= 0; i--)
373  indexes[i] = -1;
374 
375  // Build a table of (most) values, put the index in it.
376  for (int i = (int)(i0); i < (int)(i1); i++) {
377  float freqi = freq[x0 + (int)(i)];
378  int value = (int)((freqi + gain + range) / range*(maxTableSize - 1));
379  if (value < 0)
380  value = 0;
381  if (value >= maxTableSize)
382  value = maxTableSize - 1;
383  indexes[value] = i;
384  }
385  // Build from the indices an array of maxima.
386  for (int i = maxTableSize - 1; i >= 0; i--) {
387  int index = indexes[i];
388  if (index >= 0) {
389  float freqi = freq[x0 + index];
390  if (freqi < findNotesMinA)
391  break;
392 
393  bool ok = true;
394  for (int m = 0; m < maximas; m++) {
395  // Avoid to store very close maxima.
396  float maxm = maxima[m];
397  if (maxm / index < minDistance && index / maxm < minDistance) {
398  ok = false;
399  break;
400  }
401  }
402  if (ok) {
403  maxima[maximas++] = index;
404  if (maximas >= numberOfMaxima)
405  break;
406  }
407  }
408  }
409 
410 // The f2pix helper macro converts a frequency into a pixel coordinate.
411 #define f2pix(f) (logf(f)-lmins)/(lmaxs-lmins)*hiddenMid.height
412 
413  // Possibly quantize the maxima frequencies and create the pixel block limits.
414  for (int i = 0; i < maximas; i++) {
415  int index = maxima[i];
416  float f = float(index)*bin2f;
417  if (findNotesQuantize)
418  {
419  f = expf((int)(log(f / 440) / log2 * 12 - 0.5) / 12.0f*log2) * 440;
420  maxima[i] = f*f2bin;
421  }
422  float f0 = expf((log(f / 440) / log2 * 24 - 1) / 24.0f*log2) * 440;
423  maxima0[i] = f2pix(f0);
424  float f1 = expf((log(f / 440) / log2 * 24 + 1) / 24.0f*log2) * 440;
425  maxima1[i] = f2pix(f1);
426  }
427  }
428 
429  int it = 0;
430  bool inMaximum = false;
431 #endif //EXPERIMENTAL_FIND_NOTES
432 
433  for (int yy = 0; yy < hiddenMid.height; ++yy) {
434  const float bin = bins[yy];
435  const float nextBin = bins[yy+1];
436 
437  if (settings.scaleType != SpectrogramSettings::stLogarithmic) {
438  const float value = findValue
439  (freq + nBins * xx, bin, nextBin, nBins, autocorrelation, gain, range);
440  clip->mSpecPxCache->values[xx * hiddenMid.height + yy] = value;
441  }
442  else {
443  float value;
444 
445 #ifdef EXPERIMENTAL_FIND_NOTES
446  if (fftFindNotes) {
447  if (it < maximas) {
448  float i0 = maxima0[it];
449  if (yy >= i0)
450  inMaximum = true;
451 
452  if (inMaximum) {
453  float i1 = maxima1[it];
454  if (yy + 1 <= i1) {
455  value = findValue(freq + x0, bin, nextBin, nBins, autocorrelation, gain, range);
456  if (value < findNotesMinA)
457  value = minColor;
458  }
459  else {
460  it++;
461  inMaximum = false;
462  value = minColor;
463  }
464  }
465  else {
466  value = minColor;
467  }
468  }
469  else
470  value = minColor;
471  }
472  else
473 #endif //EXPERIMENTAL_FIND_NOTES
474  {
475  value = findValue
476  (freq + nBins * xx, bin, nextBin, nBins, autocorrelation, gain, range);
477  }
478  clip->mSpecPxCache->values[xx * hiddenMid.height + yy] = value;
479  } // logF
480  } // each yy
481  } // each xx
482  } // updating cache
483 
484  float selBinLo = settings.findBin( freqLo, binUnit);
485  float selBinHi = settings.findBin( freqHi, binUnit);
486  float selBinCenter = (freqLo < 0 || freqHi < 0)
487  ? -1
488  : settings.findBin( sqrt(freqLo * freqHi), binUnit );
489 
490  const bool isSpectral = settings.SpectralSelectionEnabled();
491  const bool hidden = (ZoomInfo::HIDDEN == zoomInfo.GetFisheyeState());
492  const int begin = hidden
493  ? 0
494  : std::max(0, (int)(zoomInfo.GetFisheyeLeftBoundary(-leftOffset)));
495  const int end = hidden
496  ? 0
497  : std::min(mid.width, (int)(zoomInfo.GetFisheyeRightBoundary(-leftOffset)));
498  const size_t numPixels = std::max(0, end - begin);
499 
500  SpecCache specCache;
501 
502  // need explicit resize since specCache.where[] accessed before Populate()
503  specCache.Grow(numPixels, settings, -1, t0);
504 
505  if (numPixels > 0) {
506  for (int ii = begin; ii < end; ++ii) {
507  const double time = zoomInfo.PositionToTime(ii, -leftOffset) - tOffset;
508  specCache.where[ii - begin] = sampleCount(0.5 + rate * time);
509  }
510  specCache.Populate
511  (settings, waveTrackCache,
512  0, 0, numPixels,
513  clip->GetNumSamples(),
514  tOffset, rate,
515  0 // FIXME: PRL -- make reassignment work with fisheye
516  );
517  }
518 
519  // build color gradient tables (not thread safe)
522 
523  // left pixel column of the fisheye
524  int fisheyeLeft = zoomInfo.GetFisheyeLeftBoundary(-leftOffset);
525 
526  // Bug 2389 - always draw at least one pixel of selection.
527  int selectedX = zoomInfo.TimeToPosition(selectedRegion.t0(), -leftOffset);
528 
529 #ifdef _OPENMP
530 #pragma omp parallel for
531 #endif
532  for (int xx = 0; xx < mid.width; ++xx) {
533 
534  int correctedX = xx + leftOffset - hiddenLeftOffset;
535 
536  // in fisheye mode the time scale has changed, so the row values aren't cached
537  // in the loop above, and must be fetched from fft cache
538  float* uncached;
539  if (!zoomInfo.InFisheye(xx, -leftOffset)) {
540  uncached = 0;
541  }
542  else {
543  int specIndex = (xx - fisheyeLeft) * nBins;
544  wxASSERT(specIndex >= 0 && specIndex < (int)specCache.freq.size());
545  uncached = &specCache.freq[specIndex];
546  }
547 
548  // zoomInfo must be queried for each column since with fisheye enabled
549  // time between columns is variable
550  auto w0 = sampleCount(0.5 + rate *
551  (zoomInfo.PositionToTime(xx, -leftOffset) - tOffset));
552 
553  auto w1 = sampleCount(0.5 + rate *
554  (zoomInfo.PositionToTime(xx+1, -leftOffset) - tOffset));
555 
556  bool maybeSelected = ssel0 <= w0 && w1 < ssel1;
557  maybeSelected = maybeSelected || (xx == selectedX);
558 
559  for (int yy = 0; yy < hiddenMid.height; ++yy) {
560  const float bin = bins[yy];
561  const float nextBin = bins[yy+1];
562 
563  // For spectral selection, determine what colour
564  // set to use. We use a darker selection if
565  // in both spectral range and time range.
566 
568 
569  // If we are in the time selected range, then we may use a different color set.
570  if (maybeSelected)
571  selected =
572  ChooseColorSet(bin, nextBin, selBinLo, selBinCenter, selBinHi,
573  (xx + leftOffset - hiddenLeftOffset) / DASH_LENGTH, isSpectral);
574 
575  const float value = uncached
576  ? findValue(uncached, bin, nextBin, nBins, autocorrelation, gain, range)
577  : clip->mSpecPxCache->values[correctedX * hiddenMid.height + yy];
578 
579  unsigned char rv, gv, bv;
580  GetColorGradient(value, selected, colorScheme, &rv, &gv, &bv);
581 
582 #ifdef EXPERIMENTAL_FFT_Y_GRID
583  if (fftYGrid && yGrid[yy]) {
584  rv /= 1.1f;
585  gv /= 1.1f;
586  bv /= 1.1f;
587  }
588 #endif //EXPERIMENTAL_FFT_Y_GRID
589 
590  int px = ((mid.height - 1 - yy) * mid.width + xx);
591 #ifdef EXPERIMENTAL_SPECTROGRAM_OVERLAY
592  // More transparent the closer to zero intensity.
593  alpha[px]= wxMin( 200, (value+0.3) * 500) ;
594 #endif
595  px *=3;
596  data[px++] = rv;
597  data[px++] = gv;
598  data[px] = bv;
599  } // each yy
600  } // each xx
601 
602  wxBitmap converted = wxBitmap(image);
603 
604  wxMemoryDC memDC;
605 
606  memDC.SelectObject(converted);
607 
608  dc.Blit(mid.x, mid.y, mid.width, mid.height, &memDC, 0, 0, wxCOPY, FALSE);
609 
610  // Draw clip edges, as also in waveform view, which improves the appearance
611  // of split views
612  {
613  //increase virtual view size by px to hide edges that should not be visible
614  auto clipRect = ClipParameters::GetClipRect(*clip, zoomInfo, rect.Inflate(1, 0), 1);
615  if (!clipRect.IsEmpty())
616  TrackArt::DrawClipEdges(dc, clipRect, selected);
617  }
618 }
619 
620 }
621 
623  const WaveTrack* track,
624  const WaveClip* selectedClip,
625  const wxRect & rect )
626 {
627  const auto artist = TrackArtist::Get( context );
628  const auto &blankSelectedBrush = artist->blankSelectedBrush;
629  const auto &blankBrush = artist->blankBrush;
631  context, rect, track, blankSelectedBrush, blankBrush );
632 
633  WaveTrackCache cache(track->SharedPointer<const WaveTrack>());
634  for (const auto &clip: track->GetClips())
635  DrawClipSpectrum( context, cache, clip.get(), rect, clip.get() == selectedClip );
636 
637  DrawBoldBoundaries( context, track, rect );
638 }
639 
641  TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass )
642 {
643  if ( iPass == TrackArtist::PassTracks ) {
644  auto &dc = context.dc;
645  // Update cache for locations, e.g. cutlines and merge points
646  // Bug2588: do this for both channels, even if one is not drawn, so that
647  // cut-line editing (which depends on the locations cache) works properly.
648  // If both channels are visible, we will duplicate this effort, but that
649  // matters little.
650  for( auto channel:
651  TrackList::Channels(static_cast<WaveTrack*>(FindTrack().get())) )
652  channel->UpdateLocationsCache();
653 
654  const auto wt = std::static_pointer_cast<const WaveTrack>(
655  FindTrack()->SubstitutePendingChangedTrack());
656 
657  const auto artist = TrackArtist::Get( context );
658 
659 #if defined(__WXMAC__)
660  wxAntialiasMode aamode = dc.GetGraphicsContext()->GetAntialiasMode();
661  dc.GetGraphicsContext()->SetAntialiasMode(wxANTIALIAS_NONE);
662 #endif
663 
664  auto waveTrackView = GetWaveTrackView().lock();
665  wxASSERT(waveTrackView.use_count());
666 
667  auto seletedClip = waveTrackView->GetSelectedClip().lock();
668  DoDraw( context, wt.get(), seletedClip.get(), rect );
669 
670 #if defined(__WXMAC__)
671  dc.GetGraphicsContext()->SetAntialiasMode(aamode);
672 #endif
673  }
674  WaveTrackSubView::Draw( context, rect, iPass );
675 }
676 
678  []( WaveTrackView &view ){
679  return std::make_shared< SpectrumView >( view );
680  }
681 };
682 
683 // The following attaches the spectrogram settings item to the wave track popup
684 // menu. It is appropriate only to spectrum view and so is kept in this
685 // source file with the rest of the spectrum view implementation.
686 #include "WaveTrackControls.h"
687 #include "../../../../AudioIOBase.h"
688 #include "../../../../Menus.h"
689 #include "../../../../ProjectHistory.h"
690 #include "../../../../RefreshCode.h"
691 #include "../../../../prefs/PrefsDialog.h"
692 #include "../../../../prefs/SpectrumPrefs.h"
693 #include "../../../../widgets/AudacityMessageBox.h"
694 #include "../../../../widgets/PopupMenuTable.h"
695 
696 namespace {
698 
701  {
702  static SpectrogramSettingsHandler instance;
703  return instance;
704  }
705 
706  void OnSpectrogramSettings(wxCommandEvent &);
707 
708  void InitUserData(void *pUserData) override
709  {
710  mpData = static_cast< PlayableTrackControls::InitMenuData* >(pUserData);
711  }
712 
713  void DestroyMenu() override
714  {
715  mpData = nullptr;
716  }
717 };
718 
719 void SpectrogramSettingsHandler::OnSpectrogramSettings(wxCommandEvent &)
720 {
721  class ViewSettingsDialog final : public PrefsDialog
722  {
723  public:
724  ViewSettingsDialog(wxWindow *parent, AudacityProject &project,
726  int page)
727  : PrefsDialog(parent, &project, title, factories)
728  , mPage(page)
729  {
730  }
731 
732  long GetPreferredPage() override
733  {
734  return mPage;
735  }
736 
737  void SavePreferredPage() override
738  {
739  }
740 
741  private:
742  const int mPage;
743  };
744 
745  auto gAudioIO = AudioIOBase::Get();
746  if (gAudioIO->IsBusy()){
748  XO(
749 "To change Spectrogram Settings, stop any\n playing or recording first."),
750  XO("Stop the Audio First"),
751  wxOK | wxICON_EXCLAMATION | wxCENTRE);
752  return;
753  }
754 
755  WaveTrack *const pTrack = static_cast<WaveTrack*>(mpData->pTrack);
756 
757  PrefsPanel::Factories factories;
758  // factories.push_back(WaveformPrefsFactory( pTrack ));
759  factories.push_back(SpectrumPrefsFactory( pTrack ));
760  const int page =
761  // (pTrack->GetDisplay() == WaveTrackViewConstants::Spectrum) ? 1 :
762  0;
763 
764  auto title = XO("%s:").Format( pTrack->GetName() );
765  ViewSettingsDialog dialog(
766  mpData->pParent, mpData->project, title, factories, page);
767 
768  if (0 != dialog.ShowModal()) {
769  // Redraw
770  AudacityProject *const project = &mpData->project;
771  ProjectHistory::Get( *project ).ModifyState(true);
772  //Bug 1725 Toolbar was left greyed out.
773  //This solution is overkill, but does fix the problem and is what the
774  //prefs dialog normally does.
776  mpData->result = RefreshCode::RefreshAll;
777  }
778 }
779 
782  { "SubViews/Extra" },
783  std::make_unique<PopupMenuSection>( "SpectrogramSettings",
784  // Conditionally add menu item for settings, if showing spectrum
785  PopupMenuTable::Computed< WaveTrackPopupMenuTable >(
788  static const int OnSpectrogramSettingsID =
790 
791  const auto pTrack = &table.FindWaveTrack();
792  const auto &view = WaveTrackView::Get( *pTrack );
793  const auto displays = view.GetDisplays();
794  bool hasSpectrum = (displays.end() != std::find(
795  displays.begin(), displays.end(),
796  WaveTrackSubView::Type{ WaveTrackViewConstants::Spectrum, {} }
797  ) );
798  if( hasSpectrum )
799  // In future, we might move this to the context menu of the
800  // Spectrum vertical ruler.
801  // (But the latter won't be satisfactory without a means to
802  // open that other context menu with keystrokes only, and that
803  // would require some notion of a focused sub-view.)
804  return std::make_unique<Entry>( "SpectrogramSettings",
805  Entry::Item,
806  OnSpectrogramSettingsID,
807  XXO("S&pectrogram Settings..."),
808  (wxCommandEventFunction)
809  (&SpectrogramSettingsHandler::OnSpectrogramSettings),
810  SpectrogramSettingsHandler::Instance(),
811  []( PopupMenuHandler &handler, wxMenu &menu, int id ){
812  // Bug 1253. Shouldn't open preferences if audio is busy.
813  // We can't change them on the fly yet anyway.
814  auto gAudioIO = AudioIOBase::Get();
815  menu.Enable(id, !gAudioIO->IsBusy());
816  } );
817  else
818  return nullptr;
819  } ) )
820 };
821 }
822 
WaveTrackCache
A short-lived object, during whose lifetime, the contents of the WaveTrack are assumed not to change.
Definition: WaveTrack.h:626
ProjectHistory::ModifyState
void ModifyState(bool bWantsAutoSave)
Definition: ProjectHistory.cpp:124
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
SpectrogramSettings
Spectrogram settings, either for one track or as defaults.
Definition: SpectrogramSettings.h:27
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:69
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption=XO("Message"), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: AudacityMessageBox.h:20
SpectrumView::~SpectrumView
~SpectrumView() override
WaveTrackView
Definition: WaveTrackView.h:77
RefreshCode::RefreshAll
@ RefreshAll
Definition: RefreshCode.h:26
PrefsDialog::GetPreferredPage
virtual long GetPreferredPage()=0
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
key
static const WaveTrackSubViews::RegisteredFactory key
Definition: SpectrumView.cpp:677
Profiler
A simple profiler to measure the average time lengths that a particular task/function takes....
Definition: Profiler.h:40
Track::GetName
wxString GetName() const
Definition: Track.h:410
WaveClip::GetNumSamples
sampleCount GetNumSamples() const
Definition: WaveClip.cpp:274
AColor::ColorGradientTimeSelected
@ ColorGradientTimeSelected
Definition: AColor.h:30
CommonTrackPanelCell::FindTrack
std::shared_ptr< Track > FindTrack()
Definition: CommonTrackPanelCell.h:44
TrackPanelDrawingContext
Definition: TrackPanelDrawingContext.h:22
TrackList::Channels
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1468
WaveTrackSubView::DrawBoldBoundaries
static void DrawBoldBoundaries(TrackPanelDrawingContext &context, const WaveTrack *track, const wxRect &rect)
Definition: WaveTrackView.cpp:725
SpectrumVRulerControls.h
Track::SharedPointer
std::shared_ptr< Subclass > SharedPointer()
Definition: Track.h:280
GetColorGradient
void GetColorGradient(float value, AColor::ColorGradientChoice selected, int colorScheme, unsigned char *__restrict red, unsigned char *__restrict green, unsigned char *__restrict blue)
Definition: AColor.h:141
SpectrumView::Draw
void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass) override
Definition: SpectrumView.cpp:640
anonymous_namespace{SpectrumView.cpp}::sAttachment
PopupMenuTable::AttachedItem sAttachment
Definition: SpectrumView.cpp:780
anonymous_namespace{SpectrumView.cpp}::ChooseColorSet
AColor::ColorGradientChoice ChooseColorSet(float bin0, float bin1, float selBinLo, float selBinCenter, float selBinHi, int dashCount, bool isSpectral)
Definition: SpectrumView.cpp:154
XO
#define XO(s)
Definition: Internat.h:31
TrackPanelDrawingContext::dc
wxDC & dc
Definition: TrackPanelDrawingContext.h:23
SelectedRegion::UndefinedFrequency
static const int UndefinedFrequency
Definition: SelectedRegion.h:44
WaveTrack::GetSpectrogramSettings
const SpectrogramSettings & GetSpectrogramSettings() const
Definition: WaveTrack.cpp:663
ClientData::Site::RegisteredFactory
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
GetWaveTrackMenuTable
WaveTrackPopupMenuTable & GetWaveTrackMenuTable()
Definition: WaveTrackControls.cpp:973
MenuCreator::RebuildAllMenuBars
static void RebuildAllMenuBars()
Definition: Menus.cpp:678
AColor::PreComputeGradient
static void PreComputeGradient()
Definition: AColor.cpp:640
Entry
Definition: EditToolBar.cpp:225
WaveTrackSubView::DoDetailedHitTest
std::pair< bool, std::vector< UIHandlePtr > > DoDetailedHitTest(const TrackPanelMouseState &state, const AudacityProject *pProject, int currentTool, bool bMultiTool, const std::shared_ptr< WaveTrack > &wt)
SpectrumView::DoGetVRulerControls
std::shared_ptr< TrackVRulerControls > DoGetVRulerControls() override
Definition: SpectrumView.cpp:86
WaveTrackView.h
SpecCache::Grow
void Grow(size_t len_, const SpectrogramSettings &settings, double pixelsPerSecond, double start_)
Definition: WaveClip.cpp:856
WaveClip
This allows multiple clips to be a part of one WaveTrack.
Definition: WaveClip.h:173
TrackArtist::Get
static TrackArtist * Get(TrackPanelDrawingContext &)
Definition: TrackArtist.cpp:78
XXO
#define XXO(s)
Definition: Internat.h:44
Registry::BaseItemPtr
std::unique_ptr< BaseItem > BaseItemPtr
Definition: Registry.h:71
WaveTrackSubViewType
Definition: WaveTrackViewConstants.h:91
SpecCache
Definition: WaveClip.h:38
anonymous_namespace{SpectrumView.cpp}::SpectrogramSettingsHandler::Instance
static SpectrogramSettingsHandler & Instance()
Definition: SpectrumView.cpp:700
anonymous_namespace{SpectrumView.cpp}::findValue
static float findValue(const float *spectrum, float bin0, float bin1, unsigned nBins, bool autocorrelation, int gain, int range)
Definition: SpectrumView.cpp:95
SpectrumView::SubViewType
const Type & SubViewType() const override
Definition: SpectrumView.cpp:81
WaveTrack::GetSpectrumBounds
void GetSpectrumBounds(float *min, float *max) const
Definition: WaveTrack.cpp:267
WaveTrack::GetClips
WaveClipHolders & GetClips()
Definition: WaveTrack.h:366
reg
static WaveTrackSubViewType::RegisteredType reg
Definition: SpectrumView.cpp:36
sType
static WaveTrackSubView::Type sType
Definition: SpectrumView.cpp:31
ClipParameters
Definition: WaveTrackView.h:171
AudioIOBase::Get
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:94
SpectrumView::DoDraw
static void DoDraw(TrackPanelDrawingContext &context, const WaveTrack *track, const WaveClip *selectedClip, const wxRect &rect)
Definition: SpectrumView.cpp:622
ZoomInfo::HIDDEN
@ HIDDEN
Definition: ZoomInfo.h:157
CommonTrackControls::InitMenuData
Definition: CommonTrackControls.h:34
TrackView::DoSetMinimized
virtual void DoSetMinimized(bool isMinimized)
Definition: TrackView.cpp:126
SpectrogramSettings::ScaleType
int ScaleType
Definition: SpectrogramSettings.h:57
anonymous_namespace{SpectrumView.cpp}::SpectrogramSettingsHandler
Definition: SpectrumView.cpp:697
SpectrogramSettings::stLinear
@ stLinear
Definition: SpectrogramSettings.h:59
title
static const auto title
Definition: UpdateNoticeDialog.cpp:23
SpectrumPrefsFactory
PrefsPanel::Factory SpectrumPrefsFactory(WaveTrack *wt)
Definition: SpectrumPrefs.cpp:595
SpectrogramSettings::stLogarithmic
@ stLogarithmic
Definition: SpectrogramSettings.h:60
AColor::ColorGradientUnselected
@ ColorGradientUnselected
Definition: AColor.h:29
anonymous_namespace{SpectrumView.cpp}::SpectrogramSettingsHandler::DestroyMenu
void DestroyMenu() override
Definition: SpectrumView.cpp:713
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
WaveClip::GetSpectrogram
bool GetSpectrogram(WaveTrackCache &cache, const float *&spectrogram, const sampleCount *&where, size_t numPixels, double t0, double pixelsPerSecond) const
Definition: WaveClip.cpp:1008
WaveTrackCache::GetTrack
const std::shared_ptr< const WaveTrack > & GetTrack() const
Definition: WaveTrack.h:644
TrackArt::DrawBackgroundWithSelection
AUDACITY_DLL_API void DrawBackgroundWithSelection(TrackPanelDrawingContext &context, const wxRect &rect, const Track *track, const wxBrush &selBrush, const wxBrush &unselBrush, bool useSelection=true)
Definition: TrackArtist.cpp:443
WaveTrackSubViewType::RegisteredType
Definition: WaveTrackViewConstants.h:108
PopupMenuHandler
Definition: PopupMenuTable.h:82
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:113
WaveTrackViewConstants.h
SpectrogramSettings::algPitchEAC
@ algPitchEAC
Definition: SpectrogramSettings.h:159
TrackArt::DrawClipEdges
AUDACITY_DLL_API void DrawClipEdges(wxDC &dc, const wxRect &clipRect, bool selected=false)
Definition: TrackArtist.cpp:283
WaveTrackView::Get
static WaveTrackView & Get(WaveTrack &track)
Definition: WaveTrackView.cpp:771
TrackPanelMouseState
Definition: TrackPanelMouseEvent.h:28
SpecCache::Populate
void Populate(const SpectrogramSettings &settings, WaveTrackCache &waveTrackCache, int copyBegin, int copyEnd, size_t numPixels, sampleCount numSamples, double offset, double rate, double pixelsPerSecond)
Definition: WaveClip.cpp:880
PrefsDialog
Dialog that shows the current PrefsPanel in a tabbed divider.
Definition: PrefsDialog.h:34
WaveClip::mSpecPxCache
std::unique_ptr< SpecPxCache > mSpecPxCache
Definition: WaveClip.h:359
WaveTrackViewConstants::Spectrum
@ Spectrum
Definition: WaveTrackViewConstants.h:29
WaveTrackSubView::GetWaveTrackView
std::weak_ptr< WaveTrackView > GetWaveTrackView() const
Definition: WaveTrackView.cpp:766
NumberScale::begin
Iterator begin(float nPositions) const
Definition: NumberScale.h:232
SpecCache::freq
std::vector< float > freq
Definition: WaveClip.h:89
WaveTrackPopupMenuTable::FindWaveTrack
WaveTrack & FindWaveTrack() const
Definition: WaveTrackControls.cpp:88
TrackArtist::PassTracks
@ PassTracks
Definition: TrackArtist.h:75
PopupMenuTable::AttachedItem
Definition: PopupMenuTable.h:125
AColor::ColorGradientTimeAndFrequencySelected
@ ColorGradientTimeAndFrequencySelected
Definition: AColor.h:31
SpecCache::where
std::vector< sampleCount > where
Definition: WaveClip.h:90
SpectrumView::DetailedHitTest
std::vector< UIHandlePtr > DetailedHitTest(const TrackPanelMouseState &state, const AudacityProject *pProject, int currentTool, bool bMultiTool) override
Definition: SpectrumView.cpp:45
NumberScale
Definition: NumberScale.h:32
WaveTrackControls.h
Prefs.h
params
EffectDistortion::Params params
Definition: Distortion.cpp:99
anonymous_namespace{SpectrumView.cpp}::SpectrogramSettingsHandler::InitUserData
void InitUserData(void *pUserData) override
Definition: SpectrumView.cpp:708
SpectrumView::DoSetMinimized
void DoSetMinimized(bool minimized) override
Definition: SpectrumView.cpp:56
WaveTrackPopupMenuTable
Definition: WaveTrackControls.h:74
settings
static Settings & settings()
Definition: TrackInfo.cpp:86
SpectrumView::IsSpectral
bool IsSpectral() const override
Definition: SpectrumView.cpp:40
AColor::gradient_inited
static bool gradient_inited
Definition: AColor.h:125
PopupMenuTable::Entry
PopupMenuTableEntry Entry
Definition: PopupMenuTable.h:106
anonymous_namespace{SpectrumView.cpp}::DrawClipSpectrum
void DrawClipSpectrum(TrackPanelDrawingContext &context, WaveTrackCache &waveTrackCache, const WaveClip *clip, const wxRect &rect, bool selected)
Definition: SpectrumView.cpp:172
ClipParameters::GetClipRect
static wxRect GetClipRect(const WaveClip &clip, const ZoomInfo &zoomInfo, const wxRect &viewRect, int clipOffsetX=0)
Definition: WaveTrackView.cpp:1245
ArrayOf< bool >
PrefsDialog::SavePreferredPage
virtual void SavePreferredPage()=0
PrefsPanel::Factories
std::vector< PrefsPanel::PrefsNode > Factories
Definition: PrefsPanel.h:69
WaveTrackPopupMenuTable::ReserveId
int ReserveId()
Definition: WaveTrackControls.h:78
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
AColor::ColorGradientChoice
ColorGradientChoice
Definition: AColor.h:28
PrefsDialog::PrefsDialog
PrefsDialog(wxWindow *parent, AudacityProject *pProject, const TranslatableString &titlePrefix=XO("Preferences:"), PrefsPanel::Factories &factories=PrefsPanel::DefaultFactories())
Definition: PrefsDialog.cpp:445
NumberScale::Iterator
Definition: NumberScale.h:179
SpectrumView.h
AColor::ColorGradientEdge
@ ColorGradientEdge
Definition: AColor.h:32
TrackPanelDrawable::Draw
virtual void Draw(TrackPanelDrawingContext &context, const wxRect &rect, unsigned iPass)
Definition: TrackPanelDrawable.cpp:17