Audacity 3.2.0
ProjectAudioManager.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3Audacity: A Digital Audio Editor
4
5ProjectAudioManager.cpp
6
7Paul Licameli split from ProjectManager.cpp
8
9**********************************************************************/
10
11
12#include "ProjectAudioManager.h"
13
14#include <wx/app.h>
15#include <wx/frame.h>
16#include <wx/statusbr.h>
17#include <algorithm>
18
19#include "AudioIO.h"
20#include "BasicUI.h"
21#include "CommonCommandFlags.h"
22#include "Menus.h"
23#include "Meter.h"
24#include "Mix.h"
25#include "Project.h"
26#include "ProjectAudioIO.h"
27#include "ProjectFileIO.h"
28#include "ProjectHistory.h"
29#include "ProjectRate.h"
30#include "ProjectSettings.h"
31#include "ProjectStatus.h"
32#include "ProjectWindows.h"
33#include "ScrubState.h"
34#include "TrackPanelAx.h"
35#include "UndoManager.h"
36#include "ViewInfo.h"
37#include "WaveTrack.h"
39#include "prefs/TracksPrefs.h"
40#include "tracks/ui/Scrubbing.h"
41#include "tracks/ui/TrackView.h"
44
45
46wxDEFINE_EVENT(EVT_RECORDING_DROPOUT, RecordingDropoutEvent);
47
50 []( AudacityProject &project ) {
51 return std::make_shared< ProjectAudioManager >( project );
52 }
53};
54
56{
57 return project.AttachedObjects::Get< ProjectAudioManager >(
59}
60
62 const AudacityProject &project )
63{
64 return Get( const_cast< AudacityProject & >( project ) );
65}
66
68 : mProject{ project }
69{
71 registerStatusWidthFunction{ StatusWidthFunction };
72 project.Bind( EVT_CHECKPOINT_FAILURE,
74}
75
77
79{
80 if (rate > 0) {
81 return XO("Actual Rate: %d").Format( rate );
82 }
83 else
84 // clear the status field
85 return {};
86}
87
89 const AudacityProject &project, StatusBarField field )
91{
92 if ( field == rateStatusBarField ) {
93 auto &audioManager = ProjectAudioManager::Get( project );
94 int rate = audioManager.mDisplayedRate;
95 return {
96 { { FormatRate( rate ) } },
97 50
98 };
99 }
100 return {};
101}
102
103namespace {
104// The implementation is general enough to allow backwards play too
106public:
108 double gapLeft,
109 double gapLength
110 );
112
113 void Initialize(PlaybackSchedule &schedule, double rate) override;
114
115 bool Done(PlaybackSchedule &schedule, unsigned long) override;
116
117 double OffsetTrackTime( PlaybackSchedule &schedule, double offset ) override;
118
119 PlaybackSlice GetPlaybackSlice(
120 PlaybackSchedule &schedule, size_t available) override;
121
122 std::pair<double, double> AdvancedTrackTime( PlaybackSchedule &schedule,
123 double trackTime, size_t nSamples) override;
124
125 bool RepositionPlayback(
126 PlaybackSchedule &schedule, const Mixers &playbackMixers,
127 size_t frames, size_t available) override;
128
129private:
130 double GapStart() const
131 { return mReversed ? mGapLeft + mGapLength : mGapLeft; }
132 double GapEnd() const
133 { return mReversed ? mGapLeft : mGapLeft + mGapLength; }
134 bool AtOrBefore(double trackTime1, double trackTime2) const
135 { return mReversed ? trackTime1 >= trackTime2 : trackTime1 <= trackTime2; }
136
138 const double mGapLeft, mGapLength;
139
141 double mStart = 0, mEnd = 0;
142
143 // Non-negative real time durations
144 double mDuration1 = 0, mDuration2 = 0;
145 double mInitDuration1 = 0, mInitDuration2 = 0;
146
147 bool mDiscontinuity{ false };
148 bool mReversed{ false };
149};
150
151CutPreviewPlaybackPolicy::CutPreviewPlaybackPolicy(
152 double gapLeft, double gapLength)
153: mGapLeft{ gapLeft }, mGapLength{ gapLength }
154{
155 wxASSERT(gapLength >= 0.0);
156}
157
159
161 PlaybackSchedule &schedule, double rate)
162{
163 PlaybackPolicy::Initialize(schedule, rate);
164
165 // Examine mT0 and mT1 in the schedule only now; ignore changes during play
166 double left = mStart = schedule.mT0;
167 double right = mEnd = schedule.mT1;
168 mReversed = left > right;
169 if (mReversed)
170 std::swap(left, right);
171
172 if (left < mGapLeft)
173 mDuration1 = schedule.ComputeWarpedLength(left, mGapLeft);
174 const auto gapEnd = mGapLeft + mGapLength;
175 if (gapEnd < right)
176 mDuration2 = schedule.ComputeWarpedLength(gapEnd, right);
177 if (mReversed)
179 if (sampleCount(mDuration2 * rate) == 0)
183}
184
186{
188 auto diff = schedule.GetTrackTime() - mEnd;
189 if (mReversed)
190 diff *= -1;
191 return sampleCount(diff * mRate) >= 0;
192}
193
195 PlaybackSchedule &schedule, double offset )
196{
197 // Compute new time by applying the offset, jumping over the gap
198 auto time = schedule.GetTrackTime();
199 if (offset >= 0) {
200 auto space = std::clamp(mGapLeft - time, 0.0, offset);
201 time += space;
202 offset -= space;
203 if (offset > 0)
204 time = std::max(time, mGapLeft + mGapLength) + offset;
205 }
206 else {
207 auto space = std::clamp(mGapLeft + mGapLength - time, offset, 0.0);
208 time += space;
209 offset -= space;
210 if (offset < 0)
211 time = std::min(time, mGapLeft) + offset;
212 }
213 time = std::clamp(time, std::min(mStart, mEnd), std::max(mStart, mEnd));
214
215 // Reset the durations
216 mDiscontinuity = false;
219 if (AtOrBefore(time, GapStart()))
220 mDuration1 = std::max(0.0,
221 mDuration1 - fabs(schedule.ComputeWarpedLength(mStart, time)));
222 else {
223 mDuration1 = 0;
224 mDuration2 = std::max(0.0,
225 mDuration2 - fabs(schedule.ComputeWarpedLength(GapEnd(), time)));
226 }
227
228 return time;
229}
230
232 PlaybackSchedule &, size_t available)
233{
234 size_t frames = available;
235 size_t toProduce = frames;
236 sampleCount samples1(mDuration1 * mRate + 0.5);
237 if (samples1 > 0 && samples1 < frames)
238 // Shorter slice than requested, up to the discontinuity
239 toProduce = frames = samples1.as_size_t();
240 else if (samples1 == 0) {
241 sampleCount samples2(mDuration2 * mRate + 0.5);
242 if (samples2 < frames) {
243 toProduce = samples2.as_size_t();
244 // Produce some extra silence so that the time queue consumer can
245 // satisfy its end condition
246 frames = std::min(available, toProduce + TimeQueueGrainSize + 1);
247 }
248 }
249 return { available, frames, toProduce };
250}
251
253 PlaybackSchedule &schedule, double trackTime, size_t nSamples)
254{
255 auto realDuration = nSamples / mRate;
256 if (mDuration1 > 0) {
257 mDuration1 = std::max(0.0, mDuration1 - realDuration);
258 if (sampleCount(mDuration1 * mRate) == 0) {
259 mDuration1 = 0;
260 mDiscontinuity = true;
261 return { GapStart(), GapEnd() };
262 }
263 }
264 else
265 mDuration2 = std::max(0.0, mDuration2 - realDuration);
266 if (mReversed)
267 realDuration *= -1;
268 const double time = schedule.SolveWarpedLength(trackTime, realDuration);
269
270 if ( mReversed ? time <= mEnd : time >= mEnd )
271 return {mEnd, std::numeric_limits<double>::infinity()};
272 else
273 return {time, time};
274}
275
277 const Mixers &playbackMixers, size_t, size_t )
278{
279 if (mDiscontinuity) {
280 mDiscontinuity = false;
281 auto newTime = GapEnd();
282 for (auto &pMixer : playbackMixers)
283 pMixer->Reposition(newTime, true);
284 // Tell TrackBufferExchange that we aren't done yet
285 return false;
286 }
287 return true;
288}
289}
290
292 const AudioIOStartStreamOptions &options,
293 PlayMode mode,
294 bool backwards /* = false */)
295{
296 auto &projectAudioManager = *this;
297 bool canStop = projectAudioManager.CanStopAudioStream();
298
299 if ( !canStop )
300 return -1;
301
302 auto &pStartTime = options.pStartTime;
303
304 bool nonWaveToo = options.playNonWaveTracks;
305
306 // Uncomment this for laughs!
307 // backwards = true;
308
309 double t0 = selectedRegion.t0();
310 double t1 = selectedRegion.t1();
311 // SelectedRegion guarantees t0 <= t1, so we need another boolean argument
312 // to indicate backwards play.
313 const bool newDefault = (mode == PlayMode::loopedPlay);
314
315 if (backwards)
316 std::swap(t0, t1);
317
318 projectAudioManager.SetLooping( mode == PlayMode::loopedPlay );
319 projectAudioManager.SetCutting( mode == PlayMode::cutPreviewPlay );
320
321 bool success = false;
322
323 auto gAudioIO = AudioIO::Get();
324 if (gAudioIO->IsBusy())
325 return -1;
326
327 const bool cutpreview = mode == PlayMode::cutPreviewPlay;
328 if (cutpreview && t0==t1)
329 return -1; /* msmeyer: makes no sense */
330
331 AudacityProject *p = &mProject;
332
333 auto &tracks = TrackList::Get( *p );
334
335 mLastPlayMode = mode;
336
337 bool hasaudio;
338 if (nonWaveToo)
339 hasaudio = ! tracks.Any<PlayableTrack>().empty();
340 else
341 hasaudio = ! tracks.Any<WaveTrack>().empty();
342
343 double latestEnd = tracks.GetEndTime();
344
345 if (!hasaudio)
346 return -1; // No need to continue without audio tracks
347
348#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
349 double initSeek = 0.0;
350#endif
351 double loopOffset = 0.0;
352
353 if (t1 == t0) {
354 if (newDefault) {
355 const auto &selectedRegion = ViewInfo::Get( *p ).selectedRegion;
356 // play selection if there is one, otherwise
357 // set start of play region to project start,
358 // and loop the project from current play position.
359
360 if ((t0 > selectedRegion.t0()) && (t0 < selectedRegion.t1())) {
361 t0 = selectedRegion.t0();
362 t1 = selectedRegion.t1();
363 }
364 else {
365 // loop the entire project
366 // Bug2347, loop playback from cursor position instead of project start
367 loopOffset = t0 - tracks.GetStartTime();
368 if (!pStartTime)
369 // TODO move this reassignment elsewhere so we don't need an
370 // ugly mutable member
371 pStartTime.emplace(loopOffset);
372 t0 = tracks.GetStartTime();
373 t1 = tracks.GetEndTime();
374 }
375 } else {
376 // move t0 to valid range
377 if (t0 < 0) {
378 t0 = tracks.GetStartTime();
379 }
380 else if (t0 > tracks.GetEndTime()) {
381 t0 = tracks.GetEndTime();
382 }
383#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
384 else {
385 initSeek = t0; //AC: initSeek is where playback will 'start'
386 if (!pStartTime)
387 pStartTime.emplace(initSeek);
388 t0 = tracks.GetStartTime();
389 }
390#endif
391 }
392 t1 = tracks.GetEndTime();
393 }
394 else {
395 // maybe t1 < t0, with backwards scrubbing for instance
396 if (backwards)
397 std::swap(t0, t1);
398
399 t0 = std::max(0.0, std::min(t0, latestEnd));
400 t1 = std::max(0.0, std::min(t1, latestEnd));
401
402 if (backwards)
403 std::swap(t0, t1);
404 }
405
406 int token = -1;
407
408 if (t1 != t0) {
409 if (cutpreview) {
410 const double tless = std::min(t0, t1);
411 const double tgreater = std::max(t0, t1);
412 double beforeLen, afterLen;
413 gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
414 gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
415 double tcp0 = tless-beforeLen;
416 const double diff = tgreater - tless;
417 double tcp1 = tgreater+afterLen;
418 if (backwards)
419 std::swap(tcp0, tcp1);
420 AudioIOStartStreamOptions myOptions = options;
421 myOptions.policyFactory =
422 [tless, diff](auto&) -> std::unique_ptr<PlaybackPolicy> {
423 return std::make_unique<CutPreviewPlaybackPolicy>(tless, diff);
424 };
425 token = gAudioIO->StartStream(
426 GetAllPlaybackTracks(TrackList::Get(*p), false, nonWaveToo),
427 tcp0, tcp1, tcp1, myOptions);
428 }
429 else {
430 double mixerLimit = t1;
431 if (newDefault) {
432 mixerLimit = latestEnd;
433 if (pStartTime && *pStartTime >= t1)
434 t1 = latestEnd;
435 }
436 token = gAudioIO->StartStream(
437 GetAllPlaybackTracks( tracks, false, nonWaveToo ),
438 t0, t1, mixerLimit, options);
439 }
440 if (token != 0) {
441 success = true;
443 }
444 else {
445 // Bug1627 (part of it):
446 // infinite error spew when trying to start scrub:
447 // Problem was that the error dialog yields to events,
448 // causing recursion to this function in the scrub timer
449 // handler! Easy fix, just delay the user alert instead.
450 auto &window = GetProjectFrame( mProject );
451 window.CallAfter( [&]{
452 using namespace BasicUI;
453 // Show error message if stream could not be opened
455 XO("Error"),
456 XO("Error opening sound device.\nTry changing the audio host, playback device and the project sample rate."),
457 wxT("Error_opening_sound_device"),
458 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
459 });
460 }
461 }
462
463 if (!success)
464 return -1;
465
466 return token;
467}
468
469void ProjectAudioManager::PlayCurrentRegion(bool newDefault /* = false */,
470 bool cutpreview /* = false */)
471{
472 auto &projectAudioManager = *this;
473 bool canStop = projectAudioManager.CanStopAudioStream();
474
475 if ( !canStop )
476 return;
477
478 AudacityProject *p = &mProject;
479
480 {
481
482 const auto &playRegion = ViewInfo::Get( *p ).playRegion;
483
484 if (newDefault)
485 cutpreview = false;
486 auto options = DefaultPlayOptions( *p, newDefault );
487 if (cutpreview)
488 options.envelope = nullptr;
489 auto mode =
490 cutpreview ? PlayMode::cutPreviewPlay
491 : newDefault ? PlayMode::loopedPlay
493 PlayPlayRegion(SelectedRegion(playRegion.GetStart(), playRegion.GetEnd()),
494 options,
495 mode);
496 }
497}
498
499void ProjectAudioManager::Stop(bool stopStream /* = true*/)
500{
501 AudacityProject *project = &mProject;
502 auto &projectAudioManager = *this;
503 bool canStop = projectAudioManager.CanStopAudioStream();
504
505 if ( !canStop )
506 return;
507
508 if(project) {
509 // Let scrubbing code do some appearance change
510 auto &scrubber = Scrubber::Get( *project );
511 scrubber.StopScrubbing();
512 }
513
514 auto gAudioIO = AudioIO::Get();
515
516 auto cleanup = finally( [&]{
517 projectAudioManager.SetStopping( false );
518 } );
519
520 if (stopStream && gAudioIO->IsBusy()) {
521 // flag that we are stopping
522 projectAudioManager.SetStopping( true );
523 // Allow UI to update for that
524 while( wxTheApp->ProcessIdle() )
525 ;
526 }
527
528 if(stopStream)
529 gAudioIO->StopStream();
530
531 projectAudioManager.SetLooping( false );
532 projectAudioManager.SetCutting( false );
533
534 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
535 gAudioIO->AILADisable();
536 #endif
537
538 projectAudioManager.SetPausedOff();
539 //Make sure you tell gAudioIO to unpause
540 gAudioIO->SetPaused( false );
541
542 // So that we continue monitoring after playing or recording.
543 // also clean the MeterQueues
544 if( project ) {
545 auto &projectAudioIO = ProjectAudioIO::Get( *project );
546 auto meter = projectAudioIO.GetPlaybackMeter();
547 if( meter ) {
548 meter->Clear();
549 }
550
551 meter = projectAudioIO.GetCaptureMeter();
552 if( meter ) {
553 meter->Clear();
554 }
555 }
556
557 const auto toolbar = ToolManager::Get( *project ).GetToolBar(ScrubbingBarID);
558 if (toolbar)
559 toolbar->EnableDisableButtons();
560}
561
562
564 AudacityProject &proj, bool selectedOnly, double targetRate)
565{
566 auto p = &proj;
567 size_t recordingChannels = std::max(0, AudioIORecordChannels.Read());
568 bool strictRules = (recordingChannels <= 2);
569
570 // Iterate over all wave tracks, or over selected wave tracks only.
571 // If target rate was specified, ignore all tracks with other rates.
572 //
573 // In the usual cases of one or two recording channels, seek a first-fit
574 // unbroken sub-sequence for which the total number of channels matches the
575 // required number exactly. Never drop inputs or fill only some channels
576 // of a track.
577 //
578 // In case of more than two recording channels, choose tracks only among the
579 // selected. Simply take the earliest wave tracks, until the number of
580 // channels is enough. If there are fewer channels than inputs, but at least
581 // one channel, then some of the input channels will be dropped.
582 //
583 // Resulting tracks may be non-consecutive within the list of all tracks
584 // (there may be non-wave tracks between, or non-selected tracks when
585 // considering selected tracks only.)
586
587 if (!strictRules && !selectedOnly)
588 return {};
589
590 auto &trackList = TrackList::Get( *p );
591 std::vector<unsigned> channelCounts;
592 WaveTrackArray candidates;
593 const auto range = trackList.Leaders<WaveTrack>();
594 for ( auto candidate : selectedOnly ? range + &Track::IsSelected : range ) {
595 if (targetRate != RATE_NOT_SELECTED && candidate->GetRate() != targetRate)
596 continue;
597
598 // count channels in this track
599 const auto channels = TrackList::Channels( candidate );
600 unsigned nChannels = channels.size();
601
602 if (strictRules && nChannels > recordingChannels) {
603 // The recording would under-fill this track's channels
604 // Can't use any partial accumulated results
605 // either. Keep looking.
606 candidates.clear();
607 channelCounts.clear();
608 continue;
609 }
610 else {
611 // Might use this but may have to discard some of the accumulated
612 while(strictRules &&
613 nChannels + candidates.size() > recordingChannels) {
614 auto nOldChannels = channelCounts[0];
615 wxASSERT(nOldChannels > 0);
616 channelCounts.erase(channelCounts.begin());
617 candidates.erase(candidates.begin(),
618 candidates.begin() + nOldChannels);
619 }
620 channelCounts.push_back(nChannels);
621 for ( auto channel : channels ) {
622 candidates.push_back(channel->SharedPointer<WaveTrack>());
623 if(candidates.size() == recordingChannels)
624 // Done!
625 return candidates;
626 }
627 }
628 }
629
630 if (!strictRules && !candidates.empty())
631 // good enough
632 return candidates;
633
634 // If the loop didn't exit early, we could not find enough channels
635 return {};
636}
637
639void ProjectAudioManager::OnRecord(bool altAppearance)
640{
641 bool bPreferNewTrack;
642 gPrefs->Read("/GUI/PreferNewTrackRecord", &bPreferNewTrack, false);
643 const bool appendRecord = (altAppearance == bPreferNewTrack);
644
645 // Code from CommandHandler start...
646 AudacityProject *p = &mProject;
647
648 if (p) {
649 const auto &selectedRegion = ViewInfo::Get( *p ).selectedRegion;
650 double t0 = selectedRegion.t0();
651 double t1 = selectedRegion.t1();
652 // When no time selection, recording duration is 'unlimited'.
653 if (t1 == t0)
654 t1 = DBL_MAX;
655
656 auto options = DefaultPlayOptions(*p);
657 WaveTrackArray existingTracks;
658
659 // Checking the selected tracks: counting them and
660 // making sure they all have the same rate
661 const auto selectedTracks{ GetPropertiesOfSelected(*p) };
662 const int rateOfSelected{ selectedTracks.rateOfSelected };
663 const int numberOfSelected{ selectedTracks.numberOfSelected };
664 const bool allSameRate{ selectedTracks.allSameRate };
665
666 if (!allSameRate) {
667 AudacityMessageBox(XO("The tracks selected "
668 "for recording must all have the same sampling rate"),
669 XO("Mismatched Sampling Rates"),
670 wxICON_ERROR | wxCENTRE);
671
672 return;
673 }
674
675 if (appendRecord) {
676 const auto trackRange = TrackList::Get( *p ).Any< const WaveTrack >();
677
678 // Try to find wave tracks to record into. (If any are selected,
679 // try to choose only from them; else if wave tracks exist, may record into any.)
680 existingTracks = ChooseExistingRecordingTracks(*p, true, rateOfSelected);
681 if (!existingTracks.empty()) {
682 t0 = std::max(t0,
683 (trackRange + &Track::IsSelected).max(&Track::GetEndTime));
684 }
685 else {
686 if (numberOfSelected > 0 && rateOfSelected != options.rate) {
688 "Too few tracks are selected for recording at this sample rate.\n"
689 "(Audacity requires two channels at the same sample rate for\n"
690 "each stereo track)"),
691 XO("Too Few Compatible Tracks Selected"),
692 wxICON_ERROR | wxCENTRE);
693
694 return;
695 }
696
697 existingTracks = ChooseExistingRecordingTracks(*p, false, options.rate);
698 if (!existingTracks.empty())
699 {
700 auto endTime = std::max_element(
701 existingTracks.begin(),
702 existingTracks.end(),
703 [](const auto& a, const auto& b) {
704 return a->GetEndTime() < b->GetEndTime();
705 }
706 )->get()->GetEndTime();
707
708 //If there is a suitable track, then adjust t0 so
709 //that recording not starts before the end of that track
710 t0 = std::max(t0, endTime);
711 }
712 // If suitable tracks still not found, will record into NEW ones,
713 // starting with t0
714 }
715
716 // Whether we decided on NEW tracks or not:
717 if (t1 <= selectedRegion.t0() && selectedRegion.t1() > selectedRegion.t0()) {
718 t1 = selectedRegion.t1(); // record within the selection
719 }
720 else {
721 t1 = DBL_MAX; // record for a long, long time
722 }
723 }
724
725 TransportTracks transportTracks;
726 if (UseDuplex()) {
727 // Remove recording tracks from the list of tracks for duplex ("overdub")
728 // playback.
729 /* TODO: set up stereo tracks if that is how the user has set up
730 * their preferences, and choose sample format based on prefs */
731 transportTracks = GetAllPlaybackTracks(TrackList::Get( *p ), false, true);
732 for (const auto &wt : existingTracks) {
733 auto end = transportTracks.playbackTracks.end();
734 auto it = std::find(transportTracks.playbackTracks.begin(), end, wt);
735 if (it != end)
736 transportTracks.playbackTracks.erase(it);
737 }
738 }
739
740 transportTracks.captureTracks = existingTracks;
741
742 if (rateOfSelected != RATE_NOT_SELECTED)
743 options.rate = rateOfSelected;
744
745 DoRecord(*p, transportTracks, t0, t1, altAppearance, options);
746 }
747}
748
750{
751 bool duplex;
752 gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex,
753#ifdef EXPERIMENTAL_DA
754 false
755#else
756 true
757#endif
758 );
759 return duplex;
760}
761
763 const TransportTracks &tracks,
764 double t0, double t1,
765 bool altAppearance,
766 const AudioIOStartStreamOptions &options)
767{
768 auto &projectAudioManager = *this;
769
770 CommandFlag flags = AlwaysEnabledFlag; // 0 means recalc flags.
771
772 // NB: The call may have the side effect of changing flags.
773 bool allowed = MenuManager::Get(project).TryToMakeActionAllowed(
774 flags,
776
777 if (!allowed)
778 return false;
779 // ...end of code from CommandHandler.
780
781 auto gAudioIO = AudioIO::Get();
782 if (gAudioIO->IsBusy())
783 return false;
784
785 projectAudioManager.SetAppending( !altAppearance );
786
787 bool success = false;
788
789 auto transportTracks = tracks;
790
791 // Will replace any given capture tracks with temporaries
792 transportTracks.captureTracks.clear();
793
794 const auto p = &project;
795
796 bool appendRecord = !tracks.captureTracks.empty();
797
798 auto makeNewClipName = [&](WaveTrack* track) {
799 for (auto i = 1;; ++i)
800 {
801 //i18n-hint a numerical suffix added to distinguish otherwise like-named clips when new record started
802 auto name = XC("%s #%d", "clip name template").Format(track->GetName(), i).Translation();
803 if (track->FindClipByName(name) == nullptr)
804 return name;
805 }
806 };
807
808 {
809 if (appendRecord) {
810 // Append recording:
811 // Pad selected/all wave tracks to make them all the same length
812 for (const auto &wt : tracks.captureTracks)
813 {
814 auto endTime = wt->GetEndTime();
815
816 // If the track was chosen for recording and playback both,
817 // remember the original in preroll tracks, before making the
818 // pending replacement.
819 bool prerollTrack = make_iterator_range(transportTracks.playbackTracks).contains(wt);
820 if (prerollTrack)
821 transportTracks.prerollTracks.push_back(wt);
822
823 // A function that copies all the non-sample data between
824 // wave tracks; in case the track recorded to changes scale
825 // type (for instance), during the recording.
826 auto updater = [](Track &d, const Track &s){
827 auto &dst = static_cast<WaveTrack&>(d);
828 auto &src = static_cast<const WaveTrack&>(s);
829 dst.Reinit(src);
830 };
831
832 // Get a copy of the track to be appended, to be pushed into
833 // undo history only later.
834 auto pending = std::static_pointer_cast<WaveTrack>(
836 updater, wt.get() ) );
837
838 // End of current track is before or at recording start time.
839 // Less than or equal, not just less than, to ensure a clip boundary.
840 // when append recording.
841 if (endTime <= t0) {
842 pending->CreateClip(t0, makeNewClipName(pending.get()));
843 }
844 transportTracks.captureTracks.push_back(pending);
845 }
847 }
848
849 if( transportTracks.captureTracks.empty() )
850 { // recording to NEW track(s).
851 bool recordingNameCustom, useTrackNumber, useDateStamp, useTimeStamp;
852 wxString defaultTrackName, defaultRecordingTrackName;
853
854 // Count the tracks.
855 auto &trackList = TrackList::Get( *p );
856 auto numTracks = trackList.Leaders< const WaveTrack >().size();
857
858 auto recordingChannels = std::max(1, AudioIORecordChannels.Read());
859
860 gPrefs->Read(wxT("/GUI/TrackNames/RecordingNameCustom"), &recordingNameCustom, false);
861 gPrefs->Read(wxT("/GUI/TrackNames/TrackNumber"), &useTrackNumber, false);
862 gPrefs->Read(wxT("/GUI/TrackNames/DateStamp"), &useDateStamp, false);
863 gPrefs->Read(wxT("/GUI/TrackNames/TimeStamp"), &useTimeStamp, false);
864 defaultTrackName = trackList.MakeUniqueTrackName(WaveTrack::GetDefaultAudioTrackNamePreference());
865 gPrefs->Read(wxT("/GUI/TrackNames/RecodingTrackName"), &defaultRecordingTrackName, defaultTrackName);
866
867 wxString baseTrackName = recordingNameCustom? defaultRecordingTrackName : defaultTrackName;
868
869 Track *first {};
870 for (int c = 0; c < recordingChannels; c++) {
871 auto newTrack = WaveTrackFactory::Get( *p ).Create();
872 if (!first)
873 first = newTrack.get();
874
875 // Quantize bounds to the rate of the new track.
876 if (c == 0) {
877 if (t0 < DBL_MAX)
878 t0 = newTrack->LongSamplesToTime(newTrack->TimeToLongSamples(t0));
879 if (t1 < DBL_MAX)
880 t1 = newTrack->LongSamplesToTime(newTrack->TimeToLongSamples(t1));
881 }
882
883 newTrack->SetOffset(t0);
884 wxString nameSuffix = wxString(wxT(""));
885
886 if (useTrackNumber) {
887 nameSuffix += wxString::Format(wxT("%d"), 1 + (int) numTracks + c);
888 }
889
890 if (useDateStamp) {
891 if (!nameSuffix.empty()) {
892 nameSuffix += wxT("_");
893 }
894 nameSuffix += wxDateTime::Now().FormatISODate();
895 }
896
897 if (useTimeStamp) {
898 if (!nameSuffix.empty()) {
899 nameSuffix += wxT("_");
900 }
901 nameSuffix += wxDateTime::Now().FormatISOTime();
902 }
903
904 // ISO standard would be nice, but ":" is unsafe for file name.
905 nameSuffix.Replace(wxT(":"), wxT("-"));
906
907 if (baseTrackName.empty()) {
908 newTrack->SetName(nameSuffix);
909 }
910 else if (nameSuffix.empty()) {
911 newTrack->SetName(baseTrackName);
912 }
913 else {
914 newTrack->SetName(baseTrackName + wxT("_") + nameSuffix);
915 }
916 //create a new clip with a proper name before recording is started
917 newTrack->CreateClip(t0, makeNewClipName(newTrack.get()));
918
919 TrackList::Get( *p ).RegisterPendingNewTrack( newTrack );
920
921 if ((recordingChannels > 2) &&
923 TrackView::Get( *newTrack ).SetMinimized(true);
924 }
925
926 transportTracks.captureTracks.push_back(newTrack);
927 }
928 TrackList::Get( *p ).MakeMultiChannelTrack(*first, recordingChannels, true);
929 // Bug 1548. First of new tracks needs the focus.
930 TrackFocus::Get(*p).Set(first);
931 if (TrackList::Get(*p).back())
932 TrackList::Get(*p).back()->EnsureVisible();
933 }
934
935 //Automated Input Level Adjustment Initialization
936 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
937 gAudioIO->AILAInitialize();
938 #endif
939
940 int token = gAudioIO->StartStream(transportTracks, t0, t1, t1, options);
941
942 success = (token != 0);
943
944 if (success) {
946 }
947 else {
948 CancelRecording();
949
950 // Show error message if stream could not be opened
951 auto msg = XO("Error opening recording device.\nError code: %s")
952 .Format( gAudioIO->LastPaErrorString() );
953 using namespace BasicUI;
955 XO("Error"), msg, wxT("Error_opening_sound_device"),
956 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
957 }
958 }
959
960 return success;
961}
962
964{
965 auto &projectAudioManager = *this;
966 bool canStop = projectAudioManager.CanStopAudioStream();
967
968 if ( !canStop ) {
969 return;
970 }
971
972 bool paused = !projectAudioManager.Paused();
973 TogglePaused();
974
975 auto gAudioIO = AudioIO::Get();
976
977#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
978
979 auto project = &mProject;
980 auto &scrubber = Scrubber::Get( *project );
981
982 // Bug 1494 - Pausing a seek or scrub should just STOP as
983 // it is confusing to be in a paused scrub state.
984 bool bStopInstead = paused &&
986 !scrubber.IsSpeedPlaying() &&
987 !scrubber.IsKeyboardScrubbing();
988
989 if (bStopInstead) {
990 Stop();
991 return;
992 }
993
995 scrubber.Pause(paused);
996 else
997#endif
998 {
999 gAudioIO->SetPaused(paused);
1000 }
1001}
1002
1003
1005{
1006 mPaused.fetch_xor(1, std::memory_order::memory_order_relaxed);
1007}
1008
1010{
1011 mPaused.store(0, std::memory_order::memory_order_relaxed);
1012}
1013
1015{
1016 return mPaused.load(std::memory_order_relaxed) == 1;
1017}
1018
1019
1021{
1022 const auto project = &mProject;
1023 TrackList::Get( *project ).ClearPendingTracks();
1024}
1025
1027{
1028 auto &project = mProject;
1029
1030 mDisplayedRate = rate;
1031
1032 auto display = FormatRate( rate );
1033
1034 ProjectStatus::Get( project ).Set( display, rateStatusBarField );
1035}
1036
1038{
1039 // Auto-save was done here before, but it is unnecessary, provided there
1040 // are sufficient autosaves when pushing or modifying undo states.
1041}
1042
1043// This is called after recording has stopped and all tracks have flushed.
1045{
1046 auto &project = mProject;
1047 auto &projectAudioIO = ProjectAudioIO::Get( project );
1048 auto &projectFileIO = ProjectFileIO::Get( project );
1049
1050 // Only push state if we were capturing and not monitoring
1051 if (projectAudioIO.GetAudioIOToken() > 0)
1052 {
1053 auto &history = ProjectHistory::Get( project );
1054
1055 if (IsTimerRecordCancelled()) {
1056 // discard recording
1057 history.RollbackState();
1058 // Reset timer record
1059 ResetTimerRecordCancelled();
1060 }
1061 else {
1062 // Add to history
1063 // We want this to have No-fail-guarantee if we get here from exception
1064 // handling of recording, and that means we rely on the last autosave
1065 // successfully committed to the database, not risking a failure
1066 history.PushState(XO("Recorded Audio"), XO("Record"),
1068
1069 // Now, we may add a label track to give information about
1070 // dropouts. We allow failure of this.
1071 auto gAudioIO = AudioIO::Get();
1072 auto &intervals = gAudioIO->LostCaptureIntervals();
1073 if (intervals.size()) {
1074 RecordingDropoutEvent evt{ intervals };
1075 mProject.ProcessEvent(evt);
1076 }
1077 }
1078 }
1079}
1080
1082{
1083 auto &project = mProject;
1084 auto &projectFileIO = ProjectFileIO::Get( project );
1085
1086 wxTheApp->CallAfter( [&]{ projectFileIO.AutoSave(true); });
1087}
1088
1090{
1091 const auto project = &mProject;
1092 TrackList::Get( *project ).ApplyPendingTracks();
1093}
1094
1096{
1097 auto& project = mProject;
1098 auto gAudioIO = AudioIO::Get();
1099 if (gAudioIO && &project == gAudioIO->GetOwningProject().get())
1100 {
1101 bool canStop = CanStopAudioStream();
1102
1103 gAudioIO->SetPaused(!gAudioIO->IsPaused());
1104
1105 if (canStop)
1106 {
1107 // Instead of calling ::OnPause here, we can simply do the only thing it does (i.e. toggling the pause state),
1108 // because scrubbing can not happen while recording
1109 TogglePaused();
1110 }
1111 }
1112}
1113
1115{
1116 evt.Skip();
1117 Stop();
1118}
1119
1121{
1122 auto gAudioIO = AudioIO::Get();
1123 return
1124 gAudioIO->IsBusy() &&
1125 CanStopAudioStream() &&
1126 // ... and not merely monitoring
1127 !gAudioIO->IsMonitoring() &&
1128 // ... and not punch-and-roll recording
1129 gAudioIO->GetNumCaptureChannels() == 0;
1130}
1131
1133{
1134 auto gAudioIO = AudioIO::Get();
1135 return
1136 gAudioIO->IsBusy() &&
1137 CanStopAudioStream() &&
1138 gAudioIO->GetNumCaptureChannels() > 0;
1139}
1140
1142{
1143 auto gAudioIO = AudioIO::Get();
1144 return (!gAudioIO->IsStreamActive() ||
1145 gAudioIO->IsMonitoring() ||
1146 gAudioIO->GetOwningProject().get() == &mProject );
1147}
1148
1151 [](const AudacityProject &project){
1152 auto &projectAudioManager = ProjectAudioManager::Get( project );
1153 bool canStop = projectAudioManager.CanStopAudioStream();
1154 return canStop;
1155 }
1156 }; return flag; }
1157
1159DefaultPlayOptions( AudacityProject &project, bool newDefault )
1160{
1161 auto &projectAudioIO = ProjectAudioIO::Get( project );
1162 AudioIOStartStreamOptions options { project.shared_from_this(),
1163 ProjectRate::Get( project ).GetRate() };
1164 options.captureMeter = projectAudioIO.GetCaptureMeter();
1165 options.playbackMeter = projectAudioIO.GetPlaybackMeter();
1166 options.envelope = Mixer::WarpOptions::DefaultWarp::Call(TrackList::Get(project));
1167 options.listener = ProjectAudioManager::Get( project ).shared_from_this();
1168
1169 bool loopEnabled = ViewInfo::Get(project).playRegion.Active();
1170 options.loopEnabled = loopEnabled;
1171
1172 if (newDefault) {
1173 const double trackEndTime = TrackList::Get(project).GetEndTime();
1174 const double loopEndTime = ViewInfo::Get(project).playRegion.GetEnd();
1175 options.policyFactory = [&project, trackEndTime, loopEndTime](
1176 const AudioIOStartStreamOptions &options)
1177 -> std::unique_ptr<PlaybackPolicy>
1178 {
1179 return std::make_unique<NewDefaultPlaybackPolicy>( project,
1180 trackEndTime, loopEndTime,
1181 options.loopEnabled, options.variableSpeed);
1182 };
1183
1184 // Start play from left edge of selection
1185 options.pStartTime.emplace(ViewInfo::Get(project).selectedRegion.t0());
1186 }
1187
1188 return options;
1189}
1190
1193{
1194 auto result = DefaultPlayOptions( project );
1195 auto gAudioIO = AudioIO::Get();
1196 auto PlayAtSpeedRate = gAudioIO->GetBestRate(
1197 false, //not capturing
1198 true, //is playing
1199 ProjectRate::Get( project ).GetRate() //suggested rate
1200 );
1201 result.rate = PlayAtSpeedRate;
1202 return result;
1203}
1204
1206 TrackList &trackList, bool selectedOnly, bool nonWaveToo)
1207{
1208 TransportTracks result;
1209 {
1210 auto range = trackList.Any< WaveTrack >()
1211 + (selectedOnly ? &Track::IsSelected : &Track::Any );
1212 for (auto pTrack: range)
1213 result.playbackTracks.push_back(
1214 pTrack->SharedPointer< WaveTrack >() );
1215 }
1216#ifdef EXPERIMENTAL_MIDI_OUT
1217 if (nonWaveToo) {
1218 auto range = trackList.Any< const PlayableTrack >() +
1219 (selectedOnly ? &Track::IsSelected : &Track::Any );
1220 for (auto pTrack: range)
1221 if (!track_cast<const WaveTrack *>(pTrack))
1222 result.otherPlayableTracks.push_back(
1223 pTrack->SharedPointer< const PlayableTrack >() );
1224 }
1225#else
1226 WXUNUSED(useMidi);
1227#endif
1228 return result;
1229}
1230
1231// Stop playing or recording, if paused.
1233{
1234 if( AudioIOBase::Get()->IsPaused() )
1235 Stop();
1236}
1237
1238bool ProjectAudioManager::DoPlayStopSelect( bool click, bool shift )
1239{
1240 auto &project = mProject;
1241 auto &scrubber = Scrubber::Get( project );
1242 auto token = ProjectAudioIO::Get( project ).GetAudioIOToken();
1243 auto &viewInfo = ViewInfo::Get( project );
1244 auto &selection = viewInfo.selectedRegion;
1245 auto gAudioIO = AudioIO::Get();
1246
1247 //If busy, stop playing, make sure everything is unpaused.
1248 if (scrubber.HasMark() ||
1249 gAudioIO->IsStreamActive(token)) {
1250 // change the selection
1251 auto time = gAudioIO->GetStreamTime();
1252 // Test WasSpeedPlaying(), not IsSpeedPlaying()
1253 // as we could be stopped now. Similarly WasKeyboardScrubbing().
1254 if (click && (scrubber.WasSpeedPlaying() || scrubber.WasKeyboardScrubbing()))
1255 {
1256 ;// don't change the selection.
1257 }
1258 else if (shift && click) {
1259 // Change the region selection, as if by shift-click at the play head
1260 auto t0 = selection.t0(), t1 = selection.t1();
1261 if (time < t0)
1262 // Grow selection
1263 t0 = time;
1264 else if (time > t1)
1265 // Grow selection
1266 t1 = time;
1267 else {
1268 // Shrink selection, changing the nearer boundary
1269 if (fabs(t0 - time) < fabs(t1 - time))
1270 t0 = time;
1271 else
1272 t1 = time;
1273 }
1274 selection.setTimes(t0, t1);
1275 }
1276 else if (click){
1277 // avoid a point at negative time.
1278 time = wxMax( time, 0 );
1279 // Set a point selection, as if by a click at the play head
1280 selection.setTimes(time, time);
1281 } else
1282 // How stop and set cursor always worked
1283 // -- change t0, collapsing to point only if t1 was greater
1284 selection.setT0(time, false);
1285
1286 ProjectHistory::Get( project ).ModifyState(false); // without bWantsAutoSave
1287 return true;
1288 }
1289 return false;
1290}
1291
1292// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and
1293// "OnStopSelect" merged.
1295{
1296 auto gAudioIO = AudioIO::Get();
1297 if (DoPlayStopSelect(false, false))
1298 Stop();
1299 else if (!gAudioIO->IsBusy()) {
1300 //Otherwise, start playing (assuming audio I/O isn't busy)
1301
1302 // Will automatically set mLastPlayMode
1303 PlayCurrentRegion(false);
1304 }
1305}
1306
1307#include "CommonCommandFlags.h"
1308
1310 []{ return PausedFlag(); },
1311 []{ return AudioIONotBusyFlag(); },
1312 []( const AudacityProject &project ){
1313 return MenuManager::Get( project ).mStopIfWasPaused; },
1314 []( AudacityProject &project, CommandFlag ){
1315 if ( MenuManager::Get( project ).mStopIfWasPaused )
1317 }
1318}};
1319
1320// GetSelectedProperties collects information about
1321// currently selected audio tracks
1324{
1325 double rateOfSelection{ RATE_NOT_SELECTED };
1326
1327 PropertiesOfSelected result;
1328 result.allSameRate = true;
1329
1330 const auto selectedTracks{
1331 TrackList::Get(proj).Selected< const WaveTrack >() };
1332
1333 for (const auto & track : selectedTracks)
1334 {
1335 if (rateOfSelection != RATE_NOT_SELECTED &&
1336 track->GetRate() != rateOfSelection)
1337 result.allSameRate = false;
1338 else if (rateOfSelection == RATE_NOT_SELECTED)
1339 rateOfSelection = track->GetRate();
1340 }
1341
1342 result.numberOfSelected = selectedTracks.size();
1343 result.rateOfSelected = rateOfSelection;
1344
1345 return result;
1346}
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
std::vector< std::shared_ptr< WaveTrack > > WaveTrackArray
Definition: AudioIO.h:49
IntSetting AudioIORecordChannels
Toolkit-neutral facade for basic user interface services.
constexpr CommandFlag AlwaysEnabledFlag
Definition: CommandFlag.h:35
std::bitset< NCommandFlags > CommandFlag
Definition: CommandFlag.h:31
const ReservedCommandFlag & AudioIONotBusyFlag()
const ReservedCommandFlag & PausedFlag()
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:82
#define field(n, t)
Definition: ImportAUP.cpp:167
#define XO(s)
Definition: Internat.h:31
#define XC(s, c)
Definition: Internat.h:37
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: MemoryX.h:431
constexpr size_t TimeQueueGrainSize
FileConfig * gPrefs
Definition: Prefs.cpp:71
static TranslatableString FormatRate(int rate)
wxDEFINE_EVENT(EVT_RECORDING_DROPOUT, RecordingDropoutEvent)
AudioIOStartStreamOptions DefaultPlayOptions(AudacityProject &project, bool newDefault)
AudioIOStartStreamOptions DefaultSpeedPlayOptions(AudacityProject &project)
static RegisteredMenuItemEnabler stopIfPaused
static AudacityProject::AttachedObjects::RegisteredFactory sProjectAudioManagerKey
const ReservedCommandFlag & CanStopAudioStreamFlag()
PropertiesOfSelected GetPropertiesOfSelected(const AudacityProject &proj)
@ cutPreviewPlay
constexpr int RATE_NOT_SELECTED
an object holding per-project preferred sample rate
StatusBarField
Definition: ProjectStatus.h:24
@ rateStatusBarField
Definition: ProjectStatus.h:27
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 ...
accessors for certain important windows associated with each project
@ ScrubbingBarID
Definition: ToolBar.h:79
static std::once_flag flag
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:91
static AudioIO * Get()
Definition: AudioIO.cpp:133
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:266
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
bool TryToMakeActionAllowed(CommandFlag &flags, CommandFlag flagsRqd)
Definition: Menus.cpp:720
static MenuManager & Get(AudacityProject &project)
Definition: Menus.cpp:71
bool mStopIfWasPaused
Definition: Menus.h:125
double t0() const
Definition: ViewInfo.h:34
double GetEnd() const
Definition: ViewInfo.h:134
bool Active() const
Definition: ViewInfo.h:123
AudioTrack subclass that can also be audibly replayed by the program.
Definition: Track.h:916
Directs which parts of tracks to fetch for playback.
std::vector< std::unique_ptr< Mixer > > Mixers
virtual void Initialize(PlaybackSchedule &schedule, double rate)
Called before starting an audio stream.
int GetAudioIOToken() const
void SetAudioIOToken(int token)
static ProjectAudioIO & Get(AudacityProject &project)
void Stop(bool stopStream=true)
static std::pair< TranslatableStrings, unsigned > StatusWidthFunction(const AudacityProject &project, StatusBarField field)
static WaveTrackArray ChooseExistingRecordingTracks(AudacityProject &proj, bool selectedOnly, double targetRate=RATE_NOT_SELECTED)
void OnAudioIOStartRecording() override
void OnCommitRecording() override
static TransportTracks GetAllPlaybackTracks(TrackList &trackList, bool selectedOnly, bool nonWaveToo=false)
void OnAudioIORate(int rate) override
static ProjectAudioManager & Get(AudacityProject &project)
void OnAudioIOStopRecording() override
void OnAudioIONewBlocks(const WaveTrackArray *tracks) override
~ProjectAudioManager() override
ProjectAudioManager(AudacityProject &project)
int PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode playMode, bool backwards=false)
void OnCheckpointFailure(wxCommandEvent &evt)
void PlayCurrentRegion(bool newDefault=false, bool cutpreview=false)
void OnRecord(bool altAppearance)
bool DoRecord(AudacityProject &project, const TransportTracks &transportTracks, double t0, double t1, bool altAppearance, const AudioIOStartStreamOptions &options)
void OnSoundActivationThreshold() override
static ProjectFileIO & Get(AudacityProject &project)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
static ProjectSettings & Get(AudacityProject &project)
bool GetTracksFitVerticallyZoomed() const
std::pair< std::vector< TranslatableString >, unsigned > StatusWidthResult
Definition: ProjectStatus.h:49
static ProjectStatus & Get(AudacityProject &project)
void Set(const TranslatableString &msg, StatusBarField field=mainStatusBarField)
static Scrubber & Get(AudacityProject &project)
Definition: Scrubbing.cpp:187
Defines a selected portion of a project.
double t1() const
double t0() const
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined *‍/
Definition: Prefs.h:200
virtual void EnableDisableButtons()=0
static ToolManager & Get(AudacityProject &project)
ToolBar * GetToolBar(int type) const
Track * Get()
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:225
bool IsSelected() const
Definition: Track.cpp:402
virtual double GetEndTime() const =0
bool Any() const
Definition: Track.cpp:399
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1338
void RegisterPendingNewTrack(const std::shared_ptr< Track > &pTrack)
Definition: Track.cpp:1079
bool MakeMultiChannelTrack(Track &first, int nChannels, bool aligned)
Converts channels to a multichannel track.
Definition: Track.cpp:762
double GetEndTime() const
Definition: Track.cpp:1052
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1437
bool ApplyPendingTracks()
Definition: Track.cpp:1144
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:486
std::shared_ptr< Track > RegisterPendingChangedTrack(Updater updater, Track *src)
Definition: Track.cpp:1058
void ClearPendingTracks(ListOfTracks *pAdded=nullptr)
Definition: Track.cpp:1103
void UpdatePendingTracks()
Definition: Track.cpp:1085
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1454
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1541
void SetMinimized(bool minimized)
Definition: TrackView.cpp:93
static TrackView & Get(Track &)
Definition: TrackView.cpp:69
Holds a msgid for the translation catalog; may also bind format arguments.
PlayRegion playRegion
Definition: ViewInfo.h:217
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
Definition: WaveTrack.cpp:118
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:2810
A Track that contains audio waveform data.
Definition: WaveTrack.h:57
void Reinit(const WaveTrack &orig)
Definition: WaveTrack.cpp:196
static wxString GetDefaultAudioTrackNamePreference()
Definition: WaveTrack.cpp:100
std::pair< double, double > AdvancedTrackTime(PlaybackSchedule &schedule, double trackTime, size_t nSamples) override
Compute a new point in a track's timeline from an old point and a real duration.
PlaybackSlice GetPlaybackSlice(PlaybackSchedule &schedule, size_t available) override
Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers.
double OffsetTrackTime(PlaybackSchedule &schedule, double offset) override
Called when the play head needs to jump a certain distance.
void Initialize(PlaybackSchedule &schedule, double rate) override
Called before starting an audio stream.
double mStart
Starting and ending track times set in Initialize()
bool Done(PlaybackSchedule &schedule, unsigned long) override
Returns true if schedule.GetTrackTime() has reached the end of playback.
bool RepositionPlayback(PlaybackSchedule &schedule, const Mixers &playbackMixers, size_t frames, size_t available) override
AudioIO::FillPlayBuffers calls this to update its cursors into tracks for changes of position or spee...
const double mGapLeft
Fixed at construction time; these are a track time and duration.
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
size_t as_size_t() const
Definition: SampleCount.cpp:16
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:254
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:753
struct holding stream options, including a pointer to the time warp info and AudioIOListener and whet...
Definition: AudioIOBase.h:44
PolicyFactory policyFactory
Definition: AudioIOBase.h:74
std::optional< double > pStartTime
Definition: AudioIOBase.h:59
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:49
double GetTrackTime() const
Get current track time value, unadjusted.
double mT0
Playback starts at offset of mT0, which is measured in seconds.
double mT1
Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 duri...
double SolveWarpedLength(double t0, double length) const
Compute how much unwarped time must have elapsed if length seconds of warped time has elapsed,...
double ComputeWarpedLength(double t0, double t1) const
Compute signed duration (in seconds at playback) of the specified region of the track.
Describes an amount of contiguous (but maybe time-warped) data to be extracted from tracks to play.
Notification, posted on the project, after recording has stopped, when dropouts have been detected.
static bool IsScrubbing()
Definition: ScrubState.cpp:482
PlayableTrackConstArray otherPlayableTracks
Definition: AudioIO.h:81
WaveTrackArray captureTracks
Definition: AudioIO.h:80
WaveTrackArray playbackTracks
Definition: AudioIO.h:79