Audacity 3.2.0
SpectralSelectionBar.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5SpectralSelectionBar.cpp
6
7Copyright 2014 Dominic Mazzoni
8
9This program is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2 of the License, or
12(at your option) any later version.
13
14*******************************************************************//*******************************************************************/
21
22
23
25
26#include "ToolManager.h"
27
28#include <algorithm>
29
30// For compilers that support precompilation, includes "wx/wx.h".
31#include <wx/wxprec.h>
32
33#include <wx/setup.h> // for wxUSE_* macros
34
35#ifndef WX_PRECOMP
36#include <wx/defs.h>
37#include <wx/checkbox.h>
38#include <wx/combobox.h>
39#include <wx/settings.h>
40#include <wx/sizer.h>
41#include <wx/valtext.h>
42#include <wx/window.h>
43#endif
44#include <wx/statline.h>
45
46#include "Prefs.h"
47#include "Project.h"
49#include "ProjectRate.h"
51#include "AllThemeResources.h"
52#include "SelectedRegion.h"
53#include "ViewInfo.h"
54#include "WaveTrack.h"
55
56#if wxUSE_ACCESSIBILITY
57#include "WindowAccessible.h"
58#endif
59
60#include "../widgets/NumericTextCtrl.h"
61
63
64enum {
71};
72
73BEGIN_EVENT_TABLE(SpectralSelectionBar, ToolBar)
80 EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, SpectralSelectionBar::OnUpdate)
81 EVT_COMMAND(wxID_ANY, EVT_BANDWIDTHTEXTCTRL_UPDATED, SpectralSelectionBar::OnUpdate)
84
85static const wxString preferencePath
86(wxT("/GUI/Toolbars/SpectralSelection/CenterAndWidthChoice"));
87
89{
90 return wxT("SpectralSelection");
91}
92
94: ToolBar( project, XO("Spectral Selection"), ID() )
95, mbCenterAndWidth(true)
96, mCenter(0.0), mWidth(0.0), mLow(0.0), mHigh(0.0)
97, mCenterCtrl(NULL), mWidthCtrl(NULL), mLowCtrl(NULL), mHighCtrl(NULL)
98, mChoice(NULL)
99{
102}
103
105{
106 // Do nothing, sizer deletes the controls
107}
108
110{
111 return false;
112}
113
115{
116 return BotDockID;
117}
118
120{
121 auto &toolManager = ToolManager::Get( project );
122 return *static_cast<SpectralSelectionBar*>(toolManager.GetToolBar(ID()));
123}
124
126{
127 return Get( const_cast<AudacityProject&>( project )) ;
128}
129
130void SpectralSelectionBar::Create(wxWindow * parent)
131{
132 ToolBar::Create(parent);
133 UpdatePrefs();
134 mHeight = wxWindowBase::GetSizer()->GetSize().GetHeight();
135}
136
138{
139 SetBackgroundColour( theTheme.Colour( clrMedium ) );
141
142 auto &formats = ProjectNumericFormats::Get(mProject);
143 auto frequencyFormatName = formats.GetFrequencySelectionFormatName();
144 auto bandwidthFormatName = formats.GetBandwidthSelectionFormatName();
145 wxFlexGridSizer *mainSizer = safenew wxFlexGridSizer(1, 1, 1);
146 Add(mainSizer, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, 5);
147
148 //
149 // Top row, choice box
150 //
151
152 const wxString choices[2] = {
153 _("Center frequency and Width"),
154 _("Low and High Frequencies"),
155 };
156 mChoice = safenew wxChoice
157 (this, OnChoiceID, wxDefaultPosition, wxDefaultSize, 2, choices,
158 0, wxDefaultValidator, _("Show"));
159 mChoice->SetSelection(mbCenterAndWidth ? 0 : 1);
160#if wxUSE_ACCESSIBILITY
161 // so that name can be set on a standard control
162 mChoice->SetAccessible(safenew WindowAccessible(mChoice));
163#endif
164 mChoice->SetMinSize(wxSize(mChoice->GetBestSize().x, toolbarSingle));
165
166 mainSizer->Add(mChoice, 0, wxEXPAND | wxALIGN_TOP | wxRIGHT, 6);
167
168 //
169 // Bottom row, split into two columns, each with one control
170 //
171
172 {
173 auto subSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
174
176 this, OnCenterID,
177 NumericConverterType_FREQUENCY(), frequencyFormatName, 0.0,
180 );
181 mCenterCtrl->SetName( XO("Center Frequency") );
182 subSizer->Add(mCenterCtrl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
183
186 this, OnWidthID,
187 NumericConverterType_BANDWIDTH(), bandwidthFormatName, 0.0,
189 .InvalidValue( true, -1.0 )
190 );
191 mWidthCtrl->SetName( XO("Bandwidth") );
192 subSizer->Add(mWidthCtrl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
193
196 this, OnLowID,
197 NumericConverterType_FREQUENCY(), frequencyFormatName, 0.0,
200 );
201 mLowCtrl->SetName( XO("Low Frequency") );
202 subSizer->Add(mLowCtrl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
203
206 this, OnHighID,
207 NumericConverterType_FREQUENCY(), frequencyFormatName, 0.0,
210 );
211 mHighCtrl->SetName( XO("High Frequency") );
212 subSizer->Add(mHighCtrl, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, 5);
213
218
219 mainSizer->Add(subSizer.release(), 0, wxALIGN_TOP, 0);
220 }
221
222 mainSizer->Layout();
223
224 Layout();
225
226 CallAfter([this]{
227 auto &formats = ProjectNumericFormats::Get(mProject);
229 formats.GetFrequencySelectionFormatName());
231 formats.GetBandwidthSelectionFormatName());
232 });
233}
234
236{
237 {
238 wxCommandEvent e(EVT_FREQUENCYTEXTCTRL_UPDATED);
239 e.SetString(
241 ->GetFormatName().GET());
242 OnUpdate(e);
243 }
244
246 {
247 wxCommandEvent e(EVT_BANDWIDTHTEXTCTRL_UPDATED);
248 e.SetString(mWidthCtrl->GetFormatName().GET());
249 OnUpdate(e);
250 }
251
252 // Set label to pull in language change
253 SetLabel(XO("Spectral Selection"));
254
256
257 // Give base class a chance
259}
260
261void SpectralSelectionBar::OnSize(wxSizeEvent &evt)
262{
263 Refresh(true);
264
265 evt.Skip();
266}
267
269{
273
274 double bottom, top;
275 if (mbCenterAndWidth) {
278 if ((mCenter < 0 || mWidth < 0) &&
279 (mLow >= 0 || mHigh >= 0))
280 // Transition from defined spectral selection to undefined
282 else if (mCenter < 0 && mWidth < 0)
284 else {
285 if (mCenter < 0) {
286 mWidth = log(std::min(nyq, exp(mWidth)));
287 // Choose arbitrary center for the width
288 mCenter = sqrt(nyq);
289 }
290 else if (mWidth < 0) {
291 mCenter = std::max(1.0, std::min(nyq, mCenter));
292 // Choose arbitrary width for the center
293 const double ratio = std::min(mCenter, nyq / mCenter);
294 mWidth = log(ratio * ratio);
295 }
296 else {
297 mCenter = std::max(1.0, std::min(nyq, mCenter));
298 double ratio = std::min(mCenter, nyq / mCenter);
299 mWidth = std::min(2 * log(ratio), mWidth);
300 }
301
302 const double ratio = exp(mWidth / 2);
303 bottom = mCenter / ratio, top = mCenter * ratio;
304 }
305 }
306 else {
307 bottom = mLowCtrl->GetValue();
308 top = mHighCtrl->GetValue();
309
310 if (bottom >= 0)
311 bottom = std::min(nyq, bottom);
312 else
314
315 if (top >= 0)
316 top = std::min(nyq, top);
317 else
319 // These have to be in the right order.
320 if( bottom > top ){
321 // Oops. We must fix the order.
322 if( mLowCtrl->HasFocus() )
323 top = bottom;
324 else
325 bottom = top;
326 }
327 }
328
329
330 mLow = bottom;
331 mHigh = top;
332 //SetBounds();
333
334 // Notify project and track panel, which may change
335 // the values again, and call back to us in SetFrequencies()
336 manager.ModifySpectralSelection(nyq, bottom, top, done);
337}
338
339void SpectralSelectionBar::OnCtrl(wxCommandEvent & event)
340{
341 ModifySpectralSelection(event.GetInt() != 0);
342}
343
344void SpectralSelectionBar::OnChoice(wxCommandEvent &)
345{
346 mbCenterAndWidth = (0 == mChoice->GetSelection());
348 gPrefs->Flush();
349
354
356
357 wxSize sz = GetMinSize();
358 sz.SetWidth(wxDefaultCoord);
359 SetMinSize(sz);
360 Layout();
361 Fit();
362 Updated();
363}
364
365void SpectralSelectionBar::OnIdle( wxIdleEvent &evt )
366{
367 evt.Skip();
368 auto &project = mProject;
369 const auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
370 SetFrequencies( selectedRegion.f0(), selectedRegion.f1() );
371}
372
374{
375 auto &formats = ProjectNumericFormats::Get(mProject);
376 switch (evt.type) {
379 formats.GetFrequencySelectionFormatName());
382 formats.GetBandwidthSelectionFormatName());
383 default:
384 break;
385 }
386}
387
388void SpectralSelectionBar::OnUpdate(wxCommandEvent &evt)
389{
390 auto &formats = ProjectNumericFormats::Get(mProject);
391 wxWindow *w = FindFocus();
392 bool centerFocus = (w && w == mCenterCtrl);
393 bool widthFocus = (w && w == mWidthCtrl);
394 bool lowFocus = (w && w == mLowCtrl);
395 bool highFocus = (w && w == mHighCtrl);
396
397 evt.Skip(false);
398
399 // Save formats before recreating the controls so they resize properly
400 wxEventType type = evt.GetEventType();
401 if (type == EVT_FREQUENCYTEXTCTRL_UPDATED) {
402 formats.SetFrequencySelectionFormatName(evt.GetString());
403 // Then my subscription is called
404 }
405 else if (mbCenterAndWidth &&
406 type == EVT_BANDWIDTHTEXTCTRL_UPDATED) {
407 formats.SetBandwidthSelectionFormatName(evt.GetString());
408 // Then my subscription is called
409 }
410
411 // ReCreateButtons() will get rid of our sizers and controls
412 // so reset pointers first.
413 mCenterCtrl = mWidthCtrl = NULL;
414 mLowCtrl = mHighCtrl = NULL;
415
418
419
420 if (centerFocus) {
421 mCenterCtrl->SetFocus();
422 }
423 else if (widthFocus) {
424 mWidthCtrl->SetFocus();
425 }
426 else if (lowFocus) {
427 mLowCtrl->SetFocus();
428 }
429 else if (highFocus) {
430 mHighCtrl->SetFocus();
431 }
432
433 Updated();
434}
435
437{
438 if (mbCenterAndWidth) {
441 }
442 else {
443 //Bug 1633
444 //The controls may not be able to show mHigh, e.g.
445 //if set to Hz, and in that case we should either show invalid
446 //or 'do the best we can' and truncate.
447 //If we set bounds we instead clip to the mHigh to mLow,
448 //So no SetBounds, for now.
449 //SetBounds();
452 }
453}
454
456{
457 if (mHigh >= 0)
459 else
461
462 if (mLow >= 0)
464 else
466}
467
468void SpectralSelectionBar::SetFrequencies(double bottom, double top)
469{
470 if ( mLow != bottom || mHigh != top ) {
471 mLow = bottom;
472 mHigh = top;
473
474 if (bottom > 0 && top >= bottom)
475 mWidth = log(top / bottom), mCenter = sqrt(top * bottom);
476 else
477 mWidth = mCenter = -1.0;
478
480 }
481}
482
484 const NumericFormatID &formatName)
485{
487 bool changed =
488 frequencyCtrl->SetFormatName(formatName);
489 // Test first whether changed, to avoid infinite recursion from OnUpdate
490 if (changed) {
491 wxCommandEvent e(EVT_FREQUENCYTEXTCTRL_UPDATED);
492 e.SetString(frequencyCtrl->GetFormatName().GET());
493 OnUpdate(e);
494 }
495}
496
498 const NumericFormatID &formatName)
499{
500 if (mbCenterAndWidth) {
501 bool changed =
502 mWidthCtrl->SetFormatName(formatName);
503 // Test first whether changed, to avoid infinite recursion from OnUpdate
504 if (changed) {
505 wxCommandEvent e(EVT_BANDWIDTHTEXTCTRL_UPDATED);
506 e.SetString(mWidthCtrl->GetFormatName().GET());
507 OnUpdate(e);
508 }
509 }
510}
511
515};
516
517namespace {
520 /* i18n-hint: Clicking this menu item shows the toolbar
521 for selecting a frequency range of audio */
522 wxT("ShowSpectralSelectionTB"), XXO("Spe&ctral Selection Toolbar")
523};
524}
wxT("CloseDown"))
END_EVENT_TABLE()
int min(int a, int b)
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:89
#define safenew
Definition: MemoryX.h:10
const NumericConverterType & NumericConverterType_BANDWIDTH()
const NumericConverterType & NumericConverterType_FREQUENCY()
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
an object holding per-project preferred sample rate
static const AttachedProjectObjects::RegisteredFactory manager
static RegisteredToolbarFactory factory
@ SpectralSelectionBarFirstID
IMPLEMENT_CLASS(SpectralSelectionBar, ToolBar)
static const wxString preferencePath(wxT("/GUI/Toolbars/SpectralSelection/CenterAndWidthChoice"))
const auto tracks
const auto project
THEME_API Theme theTheme
Definition: Theme.cpp:82
static constexpr auto toolbarSingle
Height of a single line toolbar.
Definition: ToolBar.h:53
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 FormatterContext ProjectContext(const AudacityProject &project)
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
const wxString & GET() const
Explicit conversion to wxString, meant to be ugly-looking and demanding of a comment why it's correct...
Definition: Identifier.h:66
void SetMinValue(double minValue)
void SetMaxValue(double maxValue)
NumericFormatID GetFormatName() const
bool Layout() override
bool SetFormatName(const NumericFormatID &formatName)
void SetName(const TranslatableString &name)
void SetValue(double newValue)
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
static ProjectNumericFormats & Get(AudacityProject &project)
static ProjectSelectionManager & Get(AudacityProject &project)
static const int UndefinedFrequency
(not quite a Toolbar) at foot of screen for setting and viewing the frequency selection range.
NumericTextCtrl * mCenterCtrl
NumericTextCtrl * mWidthCtrl
SpectralSelectionBar(AudacityProject &project)
void SetBandwidthSelectionFormatName(const NumericFormatID &formatName)
void SetFrequencies(double bottom, double top)
NumericTextCtrl * mLowCtrl
void SetFrequencySelectionFormatName(const NumericFormatID &formatName)
void OnChoice(wxCommandEvent &evt)
void OnSize(wxSizeEvent &evt)
void Create(wxWindow *parent) override
void OnUpdate(wxCommandEvent &evt)
static Identifier ID()
Observer::Subscription mFormatsSubscription
void OnIdle(wxIdleEvent &evt)
void RegenerateTooltips() override
bool ShownByDefault() const override
Whether the toolbar should be shown by default. Default implementation returns true.
DockID DefaultDockID() const override
Which dock the toolbar defaults into. Default implementation chooses the top dock.
NumericTextCtrl * mHighCtrl
void OnCtrl(wxCommandEvent &evt)
void ModifySpectralSelection(bool done=false)
static SpectralSelectionBar & Get(AudacityProject &project)
void OnFormatsChanged(ProjectNumericFormatsEvent)
wxColour & Colour(int iIndex)
Works with ToolManager and ToolDock to provide a dockable window in which buttons can be placed.
Definition: ToolBar.h:73
AudacityProject & mProject
Definition: ToolBar.h:247
DockID
Identifies one of the docking areas for toolbars.
Definition: ToolBar.h:91
@ BotDockID
Definition: ToolBar.h:93
void Add(wxWindow *window, int proportion=0, int flag=wxALIGN_TOP, int border=0, wxObject *userData=NULL)
Definition: ToolBar.cpp:709
virtual void ReCreateButtons()
Definition: ToolBar.cpp:533
void SetLabel(const wxString &label) override
Definition: ToolBar.cpp:408
void UpdatePrefs() override
Definition: ToolBar.cpp:622
void Updated()
Definition: ToolBar.cpp:684
virtual void Create(wxWindow *parent)
Definition: ToolBar.cpp:492
wxWindowPtr< ToolBar > Holder
Definition: ToolBar.h:77
static ToolManager & Get(AudacityProject &project)
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
static double ProjectNyquistFrequency(const AudacityProject &project)
Definition: WaveTrack.cpp:356
An alternative to using wxWindowAccessible, which in wxWidgets 3.1.1 contained GetParent() which was ...
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
virtual bool Read(const wxString &key, bool *value) const =0
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:383
__finl float_x4 __vecc sqrt(const float_x4 &a)
Options & InvalidValue(bool has, double v=-1.0)
enum ProjectNumericFormatsEvent::Type type