Audacity 3.2.0
TransportMenus.cpp
Go to the documentation of this file.
1
2
3#include "../AdornedRulerPanel.h"
4#include "../AudioIO.h"
5#include "../CommonCommandFlags.h"
6#include "DeviceManager.h"
7#include "../LabelTrack.h"
8#include "../Menus.h"
9#include "Prefs.h"
10#include "Project.h"
11#include "../ProjectAudioIO.h"
12#include "../ProjectAudioManager.h"
13#include "../ProjectFileIO.h"
14#include "ProjectHistory.h"
15#include "../ProjectManager.h"
16#include "ProjectRate.h"
17#include "../ProjectSettings.h"
18#include "../ProjectWindows.h"
19#include "../ProjectWindow.h"
20#include "../SelectUtilities.h"
21#include "../SoundActivatedRecord.h"
22#include "../TimerRecordDialog.h"
23#include "../TrackPanelAx.h"
24#include "../TrackPanel.h"
25#include "../TransportUtilities.h"
26#include "UndoManager.h"
27#include "../WaveClip.h"
28#include "../prefs/RecordingPrefs.h"
29#include "../prefs/TracksPrefs.h"
30#include "../WaveTrack.h"
31#include "ViewInfo.h"
32#include "../commands/CommandContext.h"
33#include "../commands/CommandManager.h"
34#include "../toolbars/ControlToolBar.h"
35#include "../toolbars/TranscriptionToolBar.h"
36#include "../widgets/AudacityMessageBox.h"
37#include "BasicUI.h"
38#include "../widgets/ProgressDialog.h"
39
40#include <thread>
41#include <float.h>
42#include <wx/app.h>
43
44// private helper classes and functions
45namespace {
46
47// TODO: Should all these functions which involve
48// the toolbar actually move into ControlToolBar?
49
55{
56 auto &toolbar = ControlToolBar::Get( project );
57 wxCommandEvent evt;
58
59 // If this project is playing, stop playing
60 auto gAudioIO = AudioIOBase::Get();
61 if (gAudioIO->IsStreamActive(
63 )) {
64 // Make momentary changes of button appearances
65 toolbar.SetPlay(false); //Pops
66 toolbar.SetStop(); //Pushes stop down
67 toolbar.OnStop(evt);
68
69 using namespace std::chrono;
70 std::this_thread::sleep_for(100ms);
71 }
72
73 // If it didn't stop playing quickly, or if some other
74 // project is playing, return
75 if (gAudioIO->IsBusy())
76 return false;
77
78 return true;
79}
80
81void DoMoveToLabel(AudacityProject &project, bool next)
82{
83 auto &tracks = TrackList::Get( project );
84 auto &trackFocus = TrackFocus::Get( project );
85 auto &window = ProjectWindow::Get( project );
86 auto &projectAudioManager = ProjectAudioManager::Get(project);
87
88 // Find the number of label tracks, and ptr to last track found
89 auto trackRange = tracks.Any<LabelTrack>();
90 auto lt = *trackRange.rbegin();
91 auto nLabelTrack = trackRange.size();
92
93 if (nLabelTrack == 0 ) {
94 trackFocus.MessageForScreenReader(XO("no label track"));
95 }
96 else if (nLabelTrack > 1) {
97 // find first label track, if any, starting at the focused track
98 lt =
99 *tracks.Find(trackFocus.Get()).Filter<LabelTrack>();
100 if (!lt)
101 trackFocus.MessageForScreenReader(
102 XO("no label track at or below focused track"));
103 }
104
105 // If there is a single label track, or there is a label track at or below
106 // the focused track
107 auto &selectedRegion = ViewInfo::Get( project ).selectedRegion;
108 if (lt) {
109 int i;
110 if (next)
111 i = lt->FindNextLabel(selectedRegion);
112 else
113 i = lt->FindPrevLabel(selectedRegion);
114
115 if (i >= 0) {
116 const LabelStruct* label = lt->GetLabel(i);
117 bool newDefault = projectAudioManager.Looping();
118 if (ProjectAudioIO::Get( project ).IsAudioActive()) {
120 selectedRegion = label->selectedRegion;
121 window.RedrawProject();
122 TransportUtilities::DoStartPlaying(project, newDefault);
123 }
124 else {
125 selectedRegion = label->selectedRegion;
126 window.ScrollIntoView(selectedRegion.t0());
127 window.RedrawProject();
128 }
129 /* i18n-hint:
130 String is replaced by the name of a label,
131 first number gives the position of that label in a sequence
132 of labels,
133 and the last number is the total number of labels in the sequence.
134 */
135 auto message = XO("%s %d of %d")
136 .Format( label->title, i + 1, lt->GetNumLabels() );
137 trackFocus.MessageForScreenReader(message);
138 }
139 else {
140 trackFocus.MessageForScreenReader(XO("no labels in label track"));
141 }
142 }
143}
144
146{
147 auto &playRegion = ViewInfo::Get(project).playRegion;
148 return playRegion.Active();
149}
150
151}
152
153// Strings for menu items and also for dialog titles
154/* i18n-hint Sets a starting point for looping play */
155static const auto SetLoopInTitle = XXO("Set Loop &In");
156/* i18n-hint Sets an ending point for looping play */
157static const auto SetLoopOutTitle = XXO("Set Loop &Out");
158
159// Menu handler functions
160
162
164
165// This plays (once, with fixed bounds) OR Stops audio. It's a toggle.
166// The default binding for Shift+SPACE.
168{
170 return;
172}
173
175{
177}
178
179// This plays (looping, maybe adjusting the loop) OR Stops audio. It's a toggle.
180// The default binding for SPACE
182{
183 auto &project = context.project;
185 return;
186
187 if( !MakeReadyToPlay(project) )
188 return;
189
190 // Now play in a loop
191 // Will automatically set mLastPlayMode
193}
194
195void OnPause(const CommandContext &context)
196{
198}
199
200void OnRecord(const CommandContext &context)
201{
202 TransportUtilities::RecordAndWait(context, false);
203}
204
205// If first choice is record same track 2nd choice is record NEW track
206// and vice versa.
208{
210}
211
212void OnTimerRecord(const CommandContext &context)
213{
214 auto &project = context.project;
215 const auto &settings = ProjectSettings::Get( project );
216 auto &undoManager = UndoManager::Get( project );
217 auto &window = ProjectWindow::Get( project );
218
219 // MY: Due to improvements in how Timer Recording saves and/or exports
220 // it is now safer to disable Timer Recording when there is more than
221 // one open project.
222 if (AllProjects{}.size() > 1) {
224 XO(
225"Timer Recording cannot be used with more than one open project.\n\nPlease close any additional projects and try again."),
226 XO("Timer Recording"),
227 wxICON_INFORMATION | wxOK);
228 return;
229 }
230
231 // MY: If the project has unsaved changes then we no longer allow access
232 // to Timer Recording. This decision has been taken as the safest approach
233 // preventing issues surrounding "dirty" projects when Automatic Save/Export
234 // is used in Timer Recording.
235 if ((undoManager.UnsavedChanges()) &&
236 (TrackList::Get( project ).Any() || settings.EmptyCanBeDirty())) {
238 XO(
239"Timer Recording cannot be used while you have unsaved changes.\n\nPlease save or close this project and try again."),
240 XO("Timer Recording"),
241 wxICON_INFORMATION | wxOK);
242 return;
243 }
244
245 // We check the selected tracks to see if there is enough of them to accommodate
246 // all input channels and all of them have the same sampling rate.
247 // Those checks will be later performed by recording function anyway,
248 // but we want to warn the user about potential problems from the very start.
249 const auto selectedTracks{ GetPropertiesOfSelected(project) };
250 const int rateOfSelected{ selectedTracks.rateOfSelected };
251 const int numberOfSelected{ selectedTracks.numberOfSelected };
252 const bool allSameRate{ selectedTracks.allSameRate };
253
254 if (!allSameRate) {
255 AudacityMessageBox(XO("The tracks selected "
256 "for recording must all have the same sampling rate"),
257 XO("Mismatched Sampling Rates"),
258 wxICON_ERROR | wxCENTRE);
259
260 return;
261 }
262
263 const auto existingTracks{ ProjectAudioManager::ChooseExistingRecordingTracks(project, true, rateOfSelected) };
264 if (existingTracks.empty()) {
265 if (numberOfSelected > 0 && rateOfSelected !=
266 ProjectRate::Get(project).GetRate()) {
268 "Too few tracks are selected for recording at this sample rate.\n"
269 "(Audacity requires two channels at the same sample rate for\n"
270 "each stereo track)"),
271 XO("Too Few Compatible Tracks Selected"),
272 wxICON_ERROR | wxCENTRE);
273
274 return;
275 }
276 }
277
278 // We use this variable to display "Current Project" in the Timer Recording
279 // save project field
280 bool bProjectSaved = !ProjectFileIO::Get( project ).IsModified();
281
282 //we break the prompting and waiting dialogs into two sections
283 //because they both give the user a chance to click cancel
284 //and therefore remove the newly inserted track.
285
286 TimerRecordDialog dialog(
287 &window, project, bProjectSaved); /* parent, project, project saved? */
288 int modalResult = dialog.ShowModal();
289 if (modalResult == wxID_CANCEL)
290 {
291 // Cancelled before recording - don't need to do anything.
292 }
293 else
294 {
295 // Bug #2382
296 // Allow recording to start at current cursor position.
297 #if 0
298 // Timer Record should not record into a selection.
299 bool bPreferNewTrack;
300 gPrefs->Read("/GUI/PreferNewTrackRecord",&bPreferNewTrack, false);
301 if (bPreferNewTrack) {
302 window.Rewind(false);
303 } else {
304 window.SkipEnd(false);
305 }
306 #endif
307
308 int iTimerRecordingOutcome = dialog.RunWaitDialog();
309 switch (iTimerRecordingOutcome) {
311 // Canceled on the wait dialog
313 break;
315 // RunWaitDialog() shows the "wait for start" as well as "recording"
316 // dialog if it returned POST_TIMER_RECORD_CANCEL it means the user
317 // cancelled while the recording, so throw out the fresh track.
318 // However, we can't undo it here because the PushState() is called in TrackPanel::OnTimer(),
319 // which is blocked by this function.
320 // so instead we mark a flag to undo it there.
322 break;
324 // No action required
325 break;
327 wxTheApp->CallAfter( []{
328 // Simulate the application Exit menu item
329 wxCommandEvent evt{ wxEVT_MENU, wxID_EXIT };
330 wxTheApp->AddPendingEvent( evt );
331 } );
333 break;
334
335#ifdef __WINDOWS__
336 case POST_TIMER_RECORD_RESTART:
337 // Restart System
339 system("shutdown /r /f /t 30");
340 break;
341 case POST_TIMER_RECORD_SHUTDOWN:
342 // Shutdown System
344 system("shutdown /s /f /t 30");
345 break;
346#endif
347 }
348 }
349}
350
351#ifdef EXPERIMENTAL_PUNCH_AND_ROLL
352void OnPunchAndRoll(const CommandContext &context)
353{
354 AudacityProject &project = context.project;
355 auto &viewInfo = ViewInfo::Get( project );
356
357 static const auto url =
358 wxT("Punch_and_Roll_Record#Using_Punch_and_Roll_Record");
359
360 auto gAudioIO = AudioIO::Get();
361 if (gAudioIO->IsBusy())
362 return;
363
364 // Ignore all but left edge of the selection.
365 viewInfo.selectedRegion.collapseToT0();
366 double t1 = std::max(0.0, viewInfo.selectedRegion.t1());
367
368 // Checking the selected tracks: making sure they all have the same rate
369 const auto selectedTracks{ GetPropertiesOfSelected(project) };
370 const int rateOfSelected{ selectedTracks.rateOfSelected };
371 const bool allSameRate{ selectedTracks.allSameRate };
372
373 if (!allSameRate) {
374 AudacityMessageBox(XO("The tracks selected "
375 "for recording must all have the same sampling rate"),
376 XO("Mismatched Sampling Rates"),
377 wxICON_ERROR | wxCENTRE);
378
379 return;
380 }
381
382 // Decide which tracks to record in.
383 auto tracks =
384 ProjectAudioManager::ChooseExistingRecordingTracks(project, true, rateOfSelected);
385 if (tracks.empty()) {
386 auto recordingChannels =
387 std::max(0, AudioIORecordChannels.Read());
388 auto message =
389 (recordingChannels == 1)
390 ? XO("Please select in a mono track.")
391 : (recordingChannels == 2)
392 ? XO("Please select in a stereo track or two mono tracks.")
393 : XO("Please select at least %d channels.").Format( recordingChannels );
395 XO("Error"), message, url);
396 return;
397 }
398
399 // Delete the portion of the target tracks right of the selection, but first,
400 // remember a part of the deletion for crossfading with the new recording.
401 // We may also adjust the starting point leftward if it is too close to the
402 // end of the track, so that at least some nonzero crossfade data can be
403 // taken.
404 PRCrossfadeData crossfadeData;
405 const double crossFadeDuration = std::max(0.0,
407 / 1000.0
408 );
409
410 // The test for t1 == 0.0 stops punch and roll deleting everything where the
411 // selection is at zero. There wouldn't be any cued audio to play in
412 // that case, so a normal record, not a punch and roll, is called for.
413 bool error = (t1 == 0.0);
414
415 double newt1 = t1;
416 for (const auto &wt : tracks) {
417 sampleCount testSample(floor(t1 * wt->GetRate()));
418 auto clip = wt->GetClipAtSample(testSample);
419 if (!clip)
420 // Bug 1890 (an enhancement request)
421 // Try again, a little to the left.
422 // Subtract 10 to allow a selection exactly at or slightly after the
423 // end time
424 clip = wt->GetClipAtSample(testSample - 10);
425 if (!clip)
426 error = true;
427 else {
428 // May adjust t1 left
429 // Let's ignore the possibility of a clip even shorter than the
430 // crossfade duration!
431 newt1 = std::min(newt1, clip->GetPlayEndTime() - crossFadeDuration);
432 }
433 }
434
435 if (error) {
436 auto message = XO("Please select a time within a clip.");
438 *ProjectFramePlacement(&project), XO("Error"), message, url);
439 return;
440 }
441
442 t1 = newt1;
443 for (const auto &wt : tracks) {
444 const auto endTime = wt->GetEndTime();
445 const auto duration =
446 std::max(0.0, std::min(crossFadeDuration, endTime - t1));
447 const size_t getLen = floor(duration * wt->GetRate());
448 std::vector<float> data(getLen);
449 if (getLen > 0) {
450 float *const samples = data.data();
451 const sampleCount pos = wt->TimeToLongSamples(t1);
452 wt->GetFloats(samples, pos, getLen);
453 }
454 crossfadeData.push_back(std::move(data));
455 }
456
457 // Change tracks only after passing the error checks above
458 for (const auto &wt : tracks) {
459 wt->Clear(t1, wt->GetEndTime());
460 }
461
462 // Choose the tracks for playback.
463 TransportTracks transportTracks;
464 const auto duplex = ProjectAudioManager::UseDuplex();
465 if (duplex)
466 // play all
467 transportTracks =
469 TrackList::Get( project ), false, true);
470 else
471 // play recording tracks only
472 std::copy(tracks.begin(), tracks.end(),
473 std::back_inserter(transportTracks.playbackTracks));
474
475 // Unlike with the usual recording, a track may be chosen both for playback
476 // and recording.
477 transportTracks.captureTracks = std::move(tracks);
478
479 // Try to start recording
480 auto options = DefaultPlayOptions( project );
481 options.rate = rateOfSelected;
482 options.preRoll = std::max(0L,
484 options.pCrossfadeData = &crossfadeData;
485 bool success = ProjectAudioManager::Get( project ).DoRecord(project,
486 transportTracks,
487 t1, DBL_MAX,
488 false, // altAppearance
489 options);
490
491 if (success)
492 // Undo state will get pushed elsewhere, when record finishes
493 ;
494 else
495 // Roll back the deletions
497}
498#endif
499
501{
503}
504
506{
508}
509
511{
512 auto &project = context.project;
513 auto &playRegion = ViewInfo::Get(project).playRegion;
514 if (!playRegion.Active())
517 true, false, SetLoopInTitle.Stripped());
518}
519
520
522{
523 auto &project = context.project;
524 auto &playRegion = ViewInfo::Get(project).playRegion;
525 if (!playRegion.Active())
528 false, false, SetLoopOutTitle.Stripped());
529}
530
532{
534}
535
536void OnRescanDevices(const CommandContext &WXUNUSED(context) )
537{
539}
540
542{
543 AudacityProject &project = context.project;
544
545 SoundActivatedRecordDialog dialog( &GetProjectFrame( project ) /* parent */ );
546 dialog.ShowModal();
547}
548
549void OnToggleSoundActivated(const CommandContext &WXUNUSED(context) )
550{
551 bool pause;
552 gPrefs->Read(wxT("/AudioIO/SoundActivatedRecord"), &pause, false);
553 gPrefs->Write(wxT("/AudioIO/SoundActivatedRecord"), !pause);
554 gPrefs->Flush();
556}
557
559{
561}
562
563void OnTogglePlayRecording(const CommandContext &WXUNUSED(context) )
564{
565 bool Duplex;
566#ifdef EXPERIMENTAL_DA
567 gPrefs->Read(wxT("/AudioIO/Duplex"), &Duplex, false);
568#else
569 gPrefs->Read(wxT("/AudioIO/Duplex"), &Duplex, true);
570#endif
571 gPrefs->Write(wxT("/AudioIO/Duplex"), !Duplex);
572 gPrefs->Flush();
574}
575
576void OnToggleSWPlaythrough(const CommandContext &WXUNUSED(context) )
577{
578 bool SWPlaythrough;
579 gPrefs->Read(wxT("/AudioIO/SWPlaythrough"), &SWPlaythrough, false);
580 gPrefs->Write(wxT("/AudioIO/SWPlaythrough"), !SWPlaythrough);
581 gPrefs->Flush();
583}
584
585#ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
586void OnToggleAutomatedInputLevelAdjustment(
587 const CommandContext &WXUNUSED(context) )
588{
589 bool AVEnabled;
590 gPrefs->Read(
591 wxT("/AudioIO/AutomatedInputLevelAdjustment"), &AVEnabled, false);
592 gPrefs->Write(wxT("/AudioIO/AutomatedInputLevelAdjustment"), !AVEnabled);
593 gPrefs->Flush();
595}
596#endif
597
598void OnStop(const CommandContext &context)
599{
601}
602
603void OnPlayOneSecond(const CommandContext &context)
604{
605 auto &project = context.project;
606 if( !MakeReadyToPlay(project) )
607 return;
608
609 auto &trackPanel = TrackPanel::Get( project );
610 auto options = DefaultPlayOptions( project );
611
612 double pos = trackPanel.GetMostRecentXPos();
614 context, SelectedRegion(pos - 0.5, pos + 0.5),
615 options, PlayMode::oneSecondPlay);
616}
617
626{
627 auto &project = context.project;
628
629 if( !MakeReadyToPlay(project) )
630 return;
631
632 auto &trackPanel = TrackPanel::Get( project );
633 auto &viewInfo = ViewInfo::Get( project );
634 const auto &selectedRegion = viewInfo.selectedRegion;
635
636 double pos = trackPanel.GetMostRecentXPos();
637
638 double t0,t1;
639 // check region between pointer and the nearest selection edge
640 if (fabs(pos - selectedRegion.t0()) <
641 fabs(pos - selectedRegion.t1())) {
642 t0 = t1 = selectedRegion.t0();
643 } else {
644 t0 = t1 = selectedRegion.t1();
645 }
646 if( pos < t1)
647 t0=pos;
648 else
649 t1=pos;
650
651 // JKC: oneSecondPlay mode disables auto scrolling
652 // On balance I think we should always do this in this function
653 // since you are typically interested in the sound EXACTLY
654 // where the cursor is.
655 // TODO: have 'playing attributes' such as 'with_autoscroll'
656 // rather than modes, since that's how we're now using the modes.
657
658 // An alternative, commented out below, is to disable autoscroll
659 // only when playing a short region, less than or equal to a second.
660// mLastPlayMode = ((t1-t0) > 1.0) ? normalPlay : oneSecondPlay;
661
662 auto playOptions = DefaultPlayOptions( project );
663
665 context, SelectedRegion(t0, t1),
666 playOptions, PlayMode::oneSecondPlay);
667}
668
669// The next 4 functions provide a limited version of the
670// functionality of OnPlayToSelection() for keyboard users
671
673{
674 auto &project = context.project;
675
676 if( !MakeReadyToPlay(project) )
677 return;
678
679 auto &viewInfo = ViewInfo::Get( project );
680 const auto &selectedRegion = viewInfo.selectedRegion;
681
682 double t0 = selectedRegion.t0();
683 double beforeLen;
684 gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
685
686 auto playOptions = DefaultPlayOptions( project );
687
689 context, SelectedRegion(t0 - beforeLen, t0),
690 playOptions, PlayMode::oneSecondPlay);
691}
692
694{
695 auto &project = context.project;
696
697 if( !MakeReadyToPlay(project) )
698 return;
699
700 auto &viewInfo = ViewInfo::Get( project );
701 const auto &selectedRegion = viewInfo.selectedRegion;
702
703 double t0 = selectedRegion.t0();
704 double t1 = selectedRegion.t1();
705 double afterLen;
706 gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
707
708 auto playOptions = DefaultPlayOptions( project );
709
710 if ( t1 - t0 > 0.0 && t1 - t0 < afterLen )
712 context, SelectedRegion(t0, t1),
713 playOptions, PlayMode::oneSecondPlay);
714 else
716 context, SelectedRegion(t0, t0 + afterLen),
717 playOptions, PlayMode::oneSecondPlay);
718}
719
721{
722 auto &project = context.project;
723
724 if( !MakeReadyToPlay(project) )
725 return;
726
727 auto &viewInfo = ViewInfo::Get( project );
728 const auto &selectedRegion = viewInfo.selectedRegion;
729
730 double t0 = selectedRegion.t0();
731 double t1 = selectedRegion.t1();
732 double beforeLen;
733 gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
734
735 auto playOptions = DefaultPlayOptions( project );
736
737 if ( t1 - t0 > 0.0 && t1 - t0 < beforeLen )
739 context, SelectedRegion(t0, t1),
740 playOptions, PlayMode::oneSecondPlay);
741 else
743 context, SelectedRegion(t1 - beforeLen, t1),
744 playOptions, PlayMode::oneSecondPlay);
745}
746
748{
749 auto &project = context.project;
750
751 if( !MakeReadyToPlay(project) )
752 return;
753
754 auto &viewInfo = ViewInfo::Get( project );
755 const auto &selectedRegion = viewInfo.selectedRegion;
756
757 double t1 = selectedRegion.t1();
758 double afterLen;
759 gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
760
761 auto playOptions = DefaultPlayOptions( project );
762
764 context, SelectedRegion(t1, t1 + afterLen),
765 playOptions, PlayMode::oneSecondPlay);
766}
767
769(const CommandContext &context)
770{
771 auto &project = context.project;
772
773 if (!MakeReadyToPlay(project))
774 return;
775
776 auto &viewInfo = ViewInfo::Get( project );
777 const auto &selectedRegion = viewInfo.selectedRegion;
778
779 double t0 = selectedRegion.t0();
780 double t1 = selectedRegion.t1();
781 double beforeLen;
782 gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
783 double afterLen;
784 gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
785
786 auto playOptions = DefaultPlayOptions( project );
787
788 if ( t1 - t0 > 0.0 && t1 - t0 < afterLen )
790 context, SelectedRegion(t0 - beforeLen, t1),
791 playOptions, PlayMode::oneSecondPlay);
792 else
794 context, SelectedRegion(t0 - beforeLen, t0 + afterLen),
795 playOptions, PlayMode::oneSecondPlay);
796}
797
799(const CommandContext &context)
800{
801 auto &project = context.project;
802
803 if (!MakeReadyToPlay(project))
804 return;
805
806 auto &viewInfo = ViewInfo::Get( project );
807 const auto &selectedRegion = viewInfo.selectedRegion;
808
809 double t0 = selectedRegion.t0();
810 double t1 = selectedRegion.t1();
811 double beforeLen;
812 gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
813 double afterLen;
814 gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
815
816 auto playOptions = DefaultPlayOptions( project );
817
818 if ( t1 - t0 > 0.0 && t1 - t0 < beforeLen )
820 context, SelectedRegion(t0, t1 + afterLen),
821 playOptions, PlayMode::oneSecondPlay);
822 else
824 context, SelectedRegion(t1 - beforeLen, t1 + afterLen),
825 playOptions, PlayMode::oneSecondPlay);
826}
827
829{
830 auto &project = context.project;
831
832 if ( !MakeReadyToPlay(project) )
833 return;
834
835 // Play with cut preview
837}
838
839void OnPlayAtSpeed(const CommandContext &context)
840{
841 auto &project = context.project;
842 auto tb = &TranscriptionToolBar::Get( project );
843
844 if (tb) {
845 tb->PlayAtSpeed(false, false);
846 }
847}
848
850{
851 auto &project = context.project;
852 auto tb = &TranscriptionToolBar::Get( project );
853
854 if (tb) {
855 tb->PlayAtSpeed(true, false);
856 }
857}
858
860{
861 auto &project = context.project;
862 auto tb = &TranscriptionToolBar::Get( project );
863
864 if (tb) {
865 tb->PlayAtSpeed(false, true);
866 }
867}
868
869void OnSetPlaySpeed(const CommandContext &context)
870{
871 auto &project = context.project;
872 auto tb = &TranscriptionToolBar::Get( project );
873
874 if (tb) {
875 tb->ShowPlaySpeedDialog();
876 }
877}
878
879void OnPlaySpeedInc(const CommandContext &context)
880{
881 auto &project = context.project;
882 auto tb = &TranscriptionToolBar::Get( project );
883
884 if (tb) {
885 tb->AdjustPlaySpeed(0.1f);
886 }
887}
888
889void OnPlaySpeedDec(const CommandContext &context)
890{
891 auto &project = context.project;
892 auto tb = &TranscriptionToolBar::Get( project );
893
894 if (tb) {
895 tb->AdjustPlaySpeed(-0.1f);
896 }
897}
898
900{
901 auto &project = context.project;
902 DoMoveToLabel(project, false);
903}
904
906{
907 auto &project = context.project;
908 DoMoveToLabel(project, true);
909}
910
911#if 0
912// Legacy handlers, not used as of version 2.3.0
913void OnStopSelect(const CommandContext &context)
914{
915 auto &project = context.project;
916 auto &history = ProjectHistory::Get( project );
917 auto &viewInfo = project.GetViewInfo();
918 auto &selectedRegion = viewInfo.selectedRegion;
919
920 auto gAudioIO = AudioIOBase::Get();
921 if (gAudioIO->IsStreamActive()) {
922 selectedRegion.setT0(gAudioIO->GetStreamTime(), false);
923 ProjectAudioManager::Get( project ).Stop();
924 history.ModifyState(false); // without bWantsAutoSave
925 }
926}
927#endif
928
929}; // struct Handler
930
931} // namespace
932
934 // Handler is not stateful. Doesn't need a factory registered with
935 // AudacityProject.
936 static TransportActions::Handler instance;
937 return instance;
938};
939
940// Menu definitions
941
942#define FN(X) (& TransportActions::Handler :: X)
943
944// Under /MenuBar
945namespace {
946using namespace MenuTable;
948{
950
951 static const auto CanStopFlags = AudioIONotBusyFlag() | CanStopAudioStreamFlag();
952
953 static BaseItemSharedPtr menu{
955 /* i18n-hint: 'Transport' is the name given to the set of controls that
956 play, record, pause etc. */
957 Menu( wxT("Transport"), XXO("Tra&nsport"),
958 Section( "Basic",
959 Menu( wxT("Play"), XXO("Pl&aying"),
960 /* i18n-hint: (verb) Start or Stop audio playback*/
961 Command( wxT("DefaultPlayStop"), XXO("Pl&ay/Stop"), FN(OnPlayDefaultOrStop),
962 CanStopAudioStreamFlag(), wxT("Space") ),
963 Command( wxT("PlayStopSelect"), XXO("Play/Stop and &Set Cursor"),
964 FN(OnPlayStopSelect), CanStopAudioStreamFlag(), wxT("X") ),
965 Command( wxT("OncePlayStop"), XXO("Play &Once/Stop"), FN(OnPlayOnceOrStop),
966 CanStopAudioStreamFlag(), wxT("Shift+Space") ),
967 Command( wxT("Pause"), XXO("&Pause"), FN(OnPause),
968 CanStopAudioStreamFlag(), wxT("P") )
969 ),
970
971 Menu( wxT("Record"), XXO("&Recording"),
972 /* i18n-hint: (verb)*/
973 Command( wxT("Record1stChoice"), XXO("&Record"), FN(OnRecord),
974 CanStopFlags, wxT("R") ),
975
976 // The OnRecord2ndChoice function is: if normal record records beside,
977 // it records below, if normal record records below, it records beside.
978 // TODO: Do 'the right thing' with other options like TimerRecord.
979 // Delayed evaluation in case gPrefs is not yet defined
980 [](const AudacityProject&)
981 { return Command( wxT("Record2ndChoice"),
982 // Our first choice is bound to R (by default)
983 // and gets the prime position.
984 // We supply the name for the 'other one' here.
985 // It should be bound to Shift+R
986 (gPrefs->ReadBool("/GUI/PreferNewTrackRecord", false)
987 ? XXO("&Append Record") : XXO("Record &New Track")),
988 FN(OnRecord2ndChoice), CanStopFlags,
989 wxT("Shift+R"),
991 ); },
992
993 Command( wxT("TimerRecord"), XXO("&Timer Record..."),
994 FN(OnTimerRecord), CanStopFlags, wxT("Shift+T") ),
995
996 #ifdef EXPERIMENTAL_PUNCH_AND_ROLL
997 Command( wxT("PunchAndRoll"), XXO("Punch and Rol&l Record"),
998 FN(OnPunchAndRoll),
999 WaveTracksExistFlag() | AudioIONotBusyFlag(), wxT("Shift+D") ),
1000 #endif
1001
1002 // JKC: I decided to duplicate this between play and record,
1003 // rather than put it at the top level.
1004 // CommandManger::AddItem can now cope with simple duplicated items.
1005 // PRL: caution, this is a duplicated command name!
1006 Command( wxT("Pause"), XXO("&Pause"), FN(OnPause),
1007 CanStopAudioStreamFlag(), wxT("P") )
1008 )
1009 ),
1010
1011 Section( "Other",
1012 Section( "",
1013 Menu( wxT("PlayRegion"), XXO("&Looping"),
1014 Command( wxT("TogglePlayRegion"), LoopToggleText,
1015 FN(OnTogglePlayRegion), AlwaysEnabledFlag,
1016 Options(L"L").CheckTest([](const AudacityProject& project){
1017 return IsLoopingEnabled(project);
1018 } )),
1019 Command( wxT("ClearPlayRegion"), XXO("&Clear Loop"),
1020 FN(OnClearPlayRegion), AlwaysEnabledFlag, L"Shift+Alt+L" ),
1021 Command( wxT("SetPlayRegionToSelection"),
1022 XXO("&Set Loop to Selection"),
1023 FN(OnSetPlayRegionToSelection), AlwaysEnabledFlag,
1024 L"Shift+L" ),
1025 Command( wxT("SetPlayRegionIn"),
1027 FN(OnSetPlayRegionIn), AlwaysEnabledFlag ),
1028 Command( wxT("SetPlayRegionOut"),
1030 FN(OnSetPlayRegionOut), AlwaysEnabledFlag )
1031 )
1032 ),
1033
1034 Command( wxT("RescanDevices"), XXO("R&escan Audio Devices"),
1035 FN(OnRescanDevices), AudioIONotBusyFlag() | CanStopAudioStreamFlag() ),
1036
1037 Menu( wxT("Options"), XXO("Transport &Options"),
1038 Section( "",
1039 // Sound Activated recording options
1040 Command( wxT("SoundActivationLevel"),
1041 XXO("Sound Activation Le&vel..."), FN(OnSoundActivated),
1043 Command( wxT("SoundActivation"),
1044 XXO("Sound A&ctivated Recording (on/off)"),
1045 FN(OnToggleSoundActivated),
1047 Options{}.CheckTest(wxT("/AudioIO/SoundActivatedRecord"), false) )
1048 ),
1049
1050 Section( "",
1051 Command( wxT("PinnedHead"), XXO("Pinned Play/Record &Head (on/off)"),
1052 FN(OnTogglePinnedHead),
1053 // Switching of scrolling on and off is permitted
1054 // even during transport
1056 Options{}.CheckTest([](const AudacityProject&){
1058
1059 Command( wxT("Overdub"), XXO("&Overdub (on/off)"),
1060 FN(OnTogglePlayRecording),
1062 Options{}.CheckTest( wxT("/AudioIO/Duplex"),
1063#ifdef EXPERIMENTAL_DA
1064 false
1065#else
1066 true
1067#endif
1068 ) ),
1069 Command( wxT("SWPlaythrough"), XXO("So&ftware Playthrough (on/off)"),
1070 FN(OnToggleSWPlaythrough),
1072 Options{}.CheckTest( wxT("/AudioIO/SWPlaythrough"), false ) )
1073
1074
1075 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
1076 ,
1077 Command( wxT("AutomatedInputLevelAdjustmentOnOff"),
1078 XXO("A&utomated Recording Level Adjustment (on/off)"),
1079 FN(OnToggleAutomatedInputLevelAdjustment),
1081 Options{}.CheckTest(
1082 wxT("/AudioIO/AutomatedInputLevelAdjustment"), false ) )
1083 #endif
1084 )
1085 )
1086 )
1087 ) ) };
1088 return menu;
1089}
1090
1092 wxT(""),
1094};
1095
1097{
1098 static BaseItemSharedPtr menu{
1100 Menu( wxT("Transport"), XXO("T&ransport"),
1101 // PlayStop is already in the menus.
1102 /* i18n-hint: (verb) Start playing audio*/
1103 Command( wxT("Play"), XXO("Pl&ay Once"), FN(OnPlayOnceOrStop),
1105 /* i18n-hint: (verb) Stop playing audio*/
1106 Command( wxT("Stop"), XXO("Sto&p"), FN(OnStop),
1108 Command( wxT("PlayOneSec"), XXO("Play &One Second"), FN(OnPlayOneSecond),
1109 CaptureNotBusyFlag(), wxT("1") ),
1110 Command( wxT("PlayToSelection"), XXO("Play to &Selection"),
1111 FN(OnPlayToSelection),
1112 CaptureNotBusyFlag(), wxT("B") ),
1113 Command( wxT("PlayBeforeSelectionStart"),
1114 XXO("Play &Before Selection Start"), FN(OnPlayBeforeSelectionStart),
1115 CaptureNotBusyFlag(), wxT("Shift+F5") ),
1116 Command( wxT("PlayAfterSelectionStart"),
1117 XXO("Play Af&ter Selection Start"), FN(OnPlayAfterSelectionStart),
1118 CaptureNotBusyFlag(), wxT("Shift+F6") ),
1119 Command( wxT("PlayBeforeSelectionEnd"),
1120 XXO("Play Be&fore Selection End"), FN(OnPlayBeforeSelectionEnd),
1121 CaptureNotBusyFlag(), wxT("Shift+F7") ),
1122 Command( wxT("PlayAfterSelectionEnd"),
1123 XXO("Play Aft&er Selection End"), FN(OnPlayAfterSelectionEnd),
1124 CaptureNotBusyFlag(), wxT("Shift+F8") ),
1125 Command( wxT("PlayBeforeAndAfterSelectionStart"),
1126 XXO("Play Before a&nd After Selection Start"),
1127 FN(OnPlayBeforeAndAfterSelectionStart), CaptureNotBusyFlag(),
1128 wxT("Ctrl+Shift+F5") ),
1129 Command( wxT("PlayBeforeAndAfterSelectionEnd"),
1130 XXO("Play Before an&d After Selection End"),
1131 FN(OnPlayBeforeAndAfterSelectionEnd), CaptureNotBusyFlag(),
1132 wxT("Ctrl+Shift+F7") ),
1133 Command( wxT("PlayCutPreview"), XXO("Play C&ut Preview"),
1134 FN(OnPlayCutPreview),
1135 CaptureNotBusyFlag(), wxT("C") )
1136 ) ) };
1137 return menu;
1138}
1139
1141 wxT("Optional/Extra/Part1"),
1143};
1144
1146{
1147 static BaseItemSharedPtr menu{
1149 Menu( wxT("PlayAtSpeed"), XXO("&Play-at-Speed"),
1150 /* i18n-hint: 'Normal Play-at-Speed' doesn't loop or cut preview. */
1151 Command( wxT("PlayAtSpeedLooped"), XXO("&Play-at-Speed"),
1152 FN(OnPlayAtSpeedLooped), CaptureNotBusyFlag() ),
1153 Command( wxT("PlayAtSpeed"), XXO("Play-at-Speed &Once"),
1154 FN(OnPlayAtSpeed), CaptureNotBusyFlag() ),
1155 Command( wxT("PlayAtSpeedCutPreview"), XXO("Play C&ut Preview-at-Speed"),
1156 FN(OnPlayAtSpeedCutPreview), CaptureNotBusyFlag() ),
1157 Command( wxT("SetPlaySpeed"), XXO("Ad&just Playback Speed..."),
1158 FN(OnSetPlaySpeed), CaptureNotBusyFlag() ),
1159 Command( wxT("PlaySpeedInc"), XXO("&Increase Playback Speed"),
1160 FN(OnPlaySpeedInc), CaptureNotBusyFlag() ),
1161 Command( wxT("PlaySpeedDec"), XXO("&Decrease Playback Speed"),
1162 FN(OnPlaySpeedDec), CaptureNotBusyFlag() )
1163 ) ) };
1164 return menu;
1165}
1166
1168 wxT("Optional/Extra/Part1"),
1170};
1171
1173{
1175 static BaseItemSharedPtr items{
1177 Items(wxT("MoveToLabel"),
1178 Command(wxT("MoveToPrevLabel"), XXO("Move to Pre&vious Label"),
1179 FN(OnMoveToPrevLabel),
1180 CaptureNotBusyFlag() | TrackPanelHasFocus(), wxT("Alt+Left")),
1181 Command(wxT("MoveToNextLabel"), XXO("Move to Ne&xt Label"),
1182 FN(OnMoveToNextLabel),
1183 CaptureNotBusyFlag() | TrackPanelHasFocus(), wxT("Alt+Right"))
1184 )) };
1185 return items;
1186}
1187
1189 { wxT("Optional/Extra/Part1/Select"), { OrderingHint::End, {} } },
1191};
1192
1193}
1194
1195#undef FN
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
IntSetting AudioIORecordChannels
Toolkit-neutral facade for basic user interface services.
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:35
wxEvtHandler CommandHandlerObject
const ReservedCommandFlag & CaptureNotBusyFlag()
const ReservedCommandFlag & AudioIOBusyFlag()
const ReservedCommandFlag & AudioIONotBusyFlag()
const ReservedCommandFlag & WaveTracksExistFlag()
const ReservedCommandFlag & TrackPanelHasFocus()
int min(int a, int b)
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
std::vector< std::vector< float > > PRCrossfadeData
FileConfig * gPrefs
Definition: Prefs.cpp:71
AudioIOStartStreamOptions DefaultPlayOptions(AudacityProject &project, bool newDefault)
const ReservedCommandFlag & CanStopAudioStreamFlag()
PropertiesOfSelected GetPropertiesOfSelected(const AudacityProject &proj)
an object holding per-project preferred sample rate
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
#define AUDIO_PRE_ROLL_KEY
#define DEFAULT_PRE_ROLL_SECONDS
#define DEFAULT_ROLL_CROSSFADE_MS
#define AUDIO_ROLL_CROSSFADE_KEY
TranslatableString label
Definition: TagsEditor.cpp:163
@ POST_TIMER_RECORD_CLOSE
@ POST_TIMER_RECORD_CANCEL
@ POST_TIMER_RECORD_CANCEL_WAIT
@ POST_TIMER_RECORD_NOTHING
static Settings & settings()
Definition: TrackInfo.cpp:87
static CommandHandlerObject & findCommandHandler(AudacityProject &)
static const auto SetLoopInTitle
static const auto SetLoopOutTitle
#define FN(X)
const TranslatableString LoopToggleText
Definition: ViewInfo.cpp:227
static AdornedRulerPanel & Get(AudacityProject &project)
size_t size() const
Definition: Project.cpp:17
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:89
static AudioIO * Get()
Definition: AudioIO.cpp:140
size_t size() const
How many attachment pointers are in the Site.
Definition: ClientData.h:251
Subclass * Find(const RegisteredFactory &key)
Get a (bare) pointer to an attachment, or null, down-cast it to Subclass *; will not create on demand...
Definition: ClientData.h:333
CommandContext provides additional information to an 'Apply()' command. It provides the project,...
AudacityProject & project
static ControlToolBar & Get(AudacityProject &project)
static DeviceManager * Instance()
Gets the singleton instance.
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
A LabelStruct holds information for ONE label in a LabelTrack.
Definition: LabelTrack.h:31
A LabelTrack is a Track that holds labels (LabelStruct).
Definition: LabelTrack.h:89
int GetNumLabels() const
Definition: LabelTrack.cpp:940
int FindPrevLabel(const SelectedRegion &currentSelection)
const LabelStruct * GetLabel(int index) const
Definition: LabelTrack.cpp:945
int FindNextLabel(const SelectedRegion &currentSelection)
static void ModifyAllProjectToolbarMenus()
Definition: Menus.cpp:592
bool Active() const
Definition: ViewInfo.h:123
int GetAudioIOToken() const
static ProjectAudioIO & Get(AudacityProject &project)
void Stop(bool stopStream=true)
static WaveTrackArray ChooseExistingRecordingTracks(AudacityProject &proj, bool selectedOnly, double targetRate=RATE_NOT_SELECTED)
static TransportTracks GetAllPlaybackTracks(TrackList &trackList, bool selectedOnly, bool nonWaveToo=false)
static ProjectAudioManager & Get(AudacityProject &project)
bool DoPlayStopSelect(bool click, bool shift)
bool DoRecord(AudacityProject &project, const TransportTracks &transportTracks, double t0, double t1, bool altAppearance, const AudioIOStartStreamOptions &options)
static ProjectFileIO & Get(AudacityProject &project)
bool IsModified() const
static ProjectHistory & Get(AudacityProject &project)
static ProjectManager & Get(AudacityProject &project)
void SetSkipSavePrompt(bool bSkip)
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
static ProjectSettings & Get(AudacityProject &project)
static ProjectWindow & Get(AudacityProject &project)
Defines a selected portion of a project.
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:185
Configures sound activated recording.
Dialog for Timer Record, i.e., timed or long recording.
int RunWaitDialog()
Runs the wait for start dialog. Returns false if the user clicks stop.
Track * Get()
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1429
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:467
static TrackPanel & Get(AudacityProject &project)
Definition: TrackPanel.cpp:227
static bool GetPinnedHeadPreference()
static TranscriptionToolBar & Get(AudacityProject &project)
static UndoManager & Get(AudacityProject &project)
Definition: UndoManager.cpp:67
PlayRegion playRegion
Definition: ViewInfo.h:217
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
void ShowErrorDialog(const WindowPlacement &placement, const TranslatableString &dlogTitle, const TranslatableString &message, const ManualPageID &helpPage, const ErrorDialogOptions &options={})
Show an error dialog with a link to the manual for further help.
Definition: BasicUI.h:241
std::unique_ptr< MenuItem > Menu(const Identifier &internalName, const TranslatableString &title, Args &&... args)
std::unique_ptr< MenuPart > Section(const Identifier &internalName, Args &&... args)
std::unique_ptr< MenuItems > Items(const Identifier &internalName, Args &&... args)
std::unique_ptr< CommandItem > Command(const CommandID &name, const TranslatableString &label_in, void(Handler::*pmf)(const CommandContext &), CommandFlag flags, const CommandManager::Options &options={}, CommandHandlerFinder finder=FinderScope::DefaultFinder())
std::shared_ptr< BaseItem > BaseItemSharedPtr
Definition: Registry.h:72
void SetPlayRegionToSelection(AudacityProject &project)
void ActivatePlayRegion(AudacityProject &project)
void OnSetRegion(AudacityProject &project, bool left, bool selection, const TranslatableString &dialogTitle)
Adjust left or right of selection or play region.
void ClearPlayRegion(AudacityProject &project)
void TogglePlayRegion(AudacityProject &project)
std::vector< CommandFlagOptions > & Options()
Definition: Menus.cpp:535
bool MakeReadyToPlay(AudacityProject &project)
void DoMoveToLabel(AudacityProject &project, bool next)
bool IsLoopingEnabled(const AudacityProject &project)
void OnSetPlayRegionIn(const CommandContext &context)
void OnPlayAtSpeedCutPreview(const CommandContext &context)
void OnPlayBeforeAndAfterSelectionEnd(const CommandContext &context)
void OnPlayBeforeSelectionStart(const CommandContext &context)
void OnMoveToPrevLabel(const CommandContext &context)
void OnSetPlayRegionToSelection(const CommandContext &context)
void OnToggleSoundActivated(const CommandContext &WXUNUSED(context))
void OnPlayCutPreview(const CommandContext &context)
void OnRecord2ndChoice(const CommandContext &context)
void OnPlayAfterSelectionStart(const CommandContext &context)
void OnTimerRecord(const CommandContext &context)
void OnTogglePlayRegion(const CommandContext &context)
void OnStop(const CommandContext &context)
void OnPlayAtSpeedLooped(const CommandContext &context)
void OnTogglePlayRecording(const CommandContext &WXUNUSED(context))
void OnSoundActivated(const CommandContext &context)
void OnPlayBeforeAndAfterSelectionStart(const CommandContext &context)
void OnSetPlaySpeed(const CommandContext &context)
void OnPlayToSelection(const CommandContext &context)
void OnPlayOneSecond(const CommandContext &context)
void OnPause(const CommandContext &context)
void OnRescanDevices(const CommandContext &WXUNUSED(context))
void OnPlayStopSelect(const CommandContext &context)
void OnPlayOnceOrStop(const CommandContext &context)
void OnPlayAtSpeed(const CommandContext &context)
void OnMoveToNextLabel(const CommandContext &context)
void OnPlayBeforeSelectionEnd(const CommandContext &context)
void OnSetPlayRegionOut(const CommandContext &context)
void OnRecord(const CommandContext &context)
void OnTogglePinnedHead(const CommandContext &context)
void OnToggleSWPlaythrough(const CommandContext &WXUNUSED(context))
void OnPlayAfterSelectionEnd(const CommandContext &context)
void OnPlaySpeedDec(const CommandContext &context)
void OnPlaySpeedInc(const CommandContext &context)
void OnPlayDefaultOrStop(const CommandContext &context)
void OnClearPlayRegion(const CommandContext &context)
WaveTrackArray captureTracks
Definition: AudioIO.h:78
WaveTrackArray playbackTracks
Definition: AudioIO.h:77
static void PlayPlayRegionAndWait(const CommandContext &context, const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode mode)
static bool DoStopPlaying(const CommandContext &context)
static void PlayCurrentRegionAndWait(const CommandContext &context, bool newDefault=false, bool cutpreview=false)
static void RecordAndWait(const CommandContext &context, bool altAppearance)
static void DoStartPlaying(const CommandContext &context, bool newDefault=false)