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#include <numeric>
19
20#include "AudioIO.h"
21#include "BasicUI.h"
22#include "CommandManager.h"
23#include "CommonCommandFlags.h"
25#include "Meter.h"
26#include "Mix.h"
27#include "PendingTracks.h"
28#include "Project.h"
29#include "ProjectAudioIO.h"
30#include "ProjectFileIO.h"
31#include "ProjectHistory.h"
32#include "ProjectRate.h"
33#include "ProjectStatus.h"
34#include "ProjectWindows.h"
35#include "ScrubState.h"
36#include "TrackFocus.h"
37#include "prefs/TracksPrefs.h"
38#include "TransportUtilities.h"
39#include "UndoManager.h"
40#include "ViewInfo.h"
41#include "Viewport.h"
42#include "WaveClip.h"
43#include "WaveTrack.h"
44#include "tracks/ui/Scrubbing.h"
47#include "AudacityMessageBox.h"
48
49
53 return std::make_shared< ProjectAudioManager >( project );
54 }
55};
56
58{
59 return project.AttachedObjects::Get< ProjectAudioManager >(
61}
62
65{
66 return Get( const_cast< AudacityProject & >( project ) );
67}
68
70 : mProject{ project }
71{
73 registerStatusWidthFunction{ StatusWidthFunction };
76}
77
79
81{
82 if (rate > 0) {
83 return XO("Actual Rate: %d").Format( rate );
84 }
85 else
86 // clear the status field
87 return {};
88}
89
93{
94 if ( field == RateStatusBarField() ) {
95 auto &audioManager = ProjectAudioManager::Get( project );
96 int rate = audioManager.mDisplayedRate;
97 return {
98 { { FormatRate( rate ) } },
99 50
100 };
101 }
102 return {};
103}
104
105namespace {
106// The implementation is general enough to allow backwards play too
108public:
110 double gapLeft,
111 double gapLength
112 );
114
115 void Initialize(PlaybackSchedule &schedule, double rate) override;
116
117 bool Done(PlaybackSchedule &schedule, unsigned long) override;
118
119 double OffsetSequenceTime( PlaybackSchedule &schedule, double offset ) override;
120
121 PlaybackSlice GetPlaybackSlice(
122 PlaybackSchedule &schedule, size_t available) override;
123
124 std::pair<double, double> AdvancedTrackTime( PlaybackSchedule &schedule,
125 double trackTime, size_t nSamples) override;
126
127 bool RepositionPlayback(
128 PlaybackSchedule &schedule, const Mixers &playbackMixers,
129 size_t frames, size_t available) override;
130
131private:
132 double GapStart() const
133 { return mReversed ? mGapLeft + mGapLength : mGapLeft; }
134 double GapEnd() const
135 { return mReversed ? mGapLeft : mGapLeft + mGapLength; }
136 bool AtOrBefore(double trackTime1, double trackTime2) const
137 { return mReversed ? trackTime1 >= trackTime2 : trackTime1 <= trackTime2; }
138
140 const double mGapLeft, mGapLength;
141
143 double mStart = 0, mEnd = 0;
144
145 // Non-negative real time durations
146 double mDuration1 = 0, mDuration2 = 0;
147 double mInitDuration1 = 0, mInitDuration2 = 0;
148
149 bool mDiscontinuity{ false };
150 bool mReversed{ false };
151};
152
153CutPreviewPlaybackPolicy::CutPreviewPlaybackPolicy(
154 double gapLeft, double gapLength)
155: mGapLeft{ gapLeft }, mGapLength{ gapLength }
156{
157 wxASSERT(gapLength >= 0.0);
158}
159
161
163 PlaybackSchedule &schedule, double rate)
164{
165 PlaybackPolicy::Initialize(schedule, rate);
166
167 // Examine mT0 and mT1 in the schedule only now; ignore changes during play
168 double left = mStart = schedule.mT0;
169 double right = mEnd = schedule.mT1;
170 mReversed = left > right;
171 if (mReversed)
172 std::swap(left, right);
173
174 if (left < mGapLeft)
175 mDuration1 = schedule.ComputeWarpedLength(left, mGapLeft);
176 const auto gapEnd = mGapLeft + mGapLength;
177 if (gapEnd < right)
178 mDuration2 = schedule.ComputeWarpedLength(gapEnd, right);
179 if (mReversed)
181 if (sampleCount(mDuration2 * rate) == 0)
185}
186
188{
190 auto diff = schedule.GetSequenceTime() - mEnd;
191 if (mReversed)
192 diff *= -1;
193 return sampleCount(diff * mRate) >= 0;
194}
195
197 PlaybackSchedule &schedule, double offset )
198{
199 // Compute new time by applying the offset, jumping over the gap
200 auto time = schedule.GetSequenceTime();
201 if (offset >= 0) {
202 auto space = std::clamp(mGapLeft - time, 0.0, offset);
203 time += space;
204 offset -= space;
205 if (offset > 0)
206 time = std::max(time, mGapLeft + mGapLength) + offset;
207 }
208 else {
209 auto space = std::clamp(mGapLeft + mGapLength - time, offset, 0.0);
210 time += space;
211 offset -= space;
212 if (offset < 0)
213 time = std::min(time, mGapLeft) + offset;
214 }
215 time = std::clamp(time, std::min(mStart, mEnd), std::max(mStart, mEnd));
216
217 // Reset the durations
218 mDiscontinuity = false;
221 if (AtOrBefore(time, GapStart()))
222 mDuration1 = std::max(0.0,
223 mDuration1 - fabs(schedule.ComputeWarpedLength(mStart, time)));
224 else {
225 mDuration1 = 0;
226 mDuration2 = std::max(0.0,
227 mDuration2 - fabs(schedule.ComputeWarpedLength(GapEnd(), time)));
228 }
229
230 return time;
231}
232
234 PlaybackSchedule &, size_t available)
235{
236 size_t frames = available;
237 size_t toProduce = frames;
238 sampleCount samples1(mDuration1 * mRate + 0.5);
239 if (samples1 > 0 && samples1 < frames)
240 // Shorter slice than requested, up to the discontinuity
241 toProduce = frames = samples1.as_size_t();
242 else if (samples1 == 0) {
243 sampleCount samples2(mDuration2 * mRate + 0.5);
244 if (samples2 < frames) {
245 toProduce = samples2.as_size_t();
246 // Produce some extra silence so that the time queue consumer can
247 // satisfy its end condition
248 frames = std::min(available, toProduce + TimeQueueGrainSize + 1);
249 }
250 }
251 return { available, frames, toProduce };
252}
253
255 PlaybackSchedule &schedule, double trackTime, size_t nSamples)
256{
257 auto realDuration = nSamples / mRate;
258 if (mDuration1 > 0) {
259 mDuration1 = std::max(0.0, mDuration1 - realDuration);
260 if (sampleCount(mDuration1 * mRate) == 0) {
261 mDuration1 = 0;
262 mDiscontinuity = true;
263 return { GapStart(), GapEnd() };
264 }
265 }
266 else
267 mDuration2 = std::max(0.0, mDuration2 - realDuration);
268 if (mReversed)
269 realDuration *= -1;
270 const double time = schedule.SolveWarpedLength(trackTime, realDuration);
271
272 if ( mReversed ? time <= mEnd : time >= mEnd )
273 return {mEnd, std::numeric_limits<double>::infinity()};
274 else
275 return {time, time};
276}
277
279 const Mixers &playbackMixers, size_t, size_t )
280{
281 if (mDiscontinuity) {
282 mDiscontinuity = false;
283 auto newTime = GapEnd();
284 for (auto &pMixer : playbackMixers)
285 pMixer->Reposition(newTime, true);
286 // Tell SequenceBufferExchange that we aren't done yet
287 return false;
288 }
289 return true;
290}
291}
292
294 const AudioIOStartStreamOptions &options,
295 PlayMode mode,
296 bool backwards /* = false */)
297{
298 auto &projectAudioManager = *this;
299 bool canStop = projectAudioManager.CanStopAudioStream();
300
301 if ( !canStop )
302 return -1;
303
304 auto &pStartTime = options.pStartTime;
305
306 bool nonWaveToo = options.playNonWaveTracks;
307
308 // Uncomment this for laughs!
309 // backwards = true;
310
311 double t0 = selectedRegion.t0();
312 double t1 = selectedRegion.t1();
313 // SelectedRegion guarantees t0 <= t1, so we need another boolean argument
314 // to indicate backwards play.
315 const bool newDefault = (mode == PlayMode::loopedPlay);
316
317 if (backwards)
318 std::swap(t0, t1);
319
320 projectAudioManager.SetLooping( mode == PlayMode::loopedPlay );
321 projectAudioManager.SetCutting( mode == PlayMode::cutPreviewPlay );
322
323 bool success = false;
324
325 auto gAudioIO = AudioIO::Get();
326 if (gAudioIO->IsBusy())
327 return -1;
328
329 const bool cutpreview = mode == PlayMode::cutPreviewPlay;
330 if (cutpreview && t0==t1)
331 return -1; /* msmeyer: makes no sense */
332
333 AudacityProject *p = &mProject;
334
335 auto &tracks = TrackList::Get(*p);
336
337 mLastPlayMode = mode;
338
339 bool hasaudio;
340 if (nonWaveToo)
341 hasaudio = ! tracks.Any<PlayableTrack>().empty();
342 else
343 hasaudio = ! tracks.Any<WaveTrack>().empty();
344
345 double latestEnd = tracks.GetEndTime();
346
347 if (!hasaudio)
348 return -1; // No need to continue without audio tracks
349
350#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
351 double initSeek = 0.0;
352#endif
353 double loopOffset = 0.0;
354
355 if (t1 == t0) {
356 if (newDefault) {
357 const auto &selectedRegion = ViewInfo::Get( *p ).selectedRegion;
358 // play selection if there is one, otherwise
359 // set start of play region to project start,
360 // and loop the project from current play position.
361
362 if ((t0 > selectedRegion.t0()) && (t0 < selectedRegion.t1())) {
363 t0 = selectedRegion.t0();
364 t1 = selectedRegion.t1();
365 }
366 else {
367 // loop the entire project
368 // Bug2347, loop playback from cursor position instead of project start
369 loopOffset = t0 - tracks.GetStartTime();
370 if (!pStartTime)
371 // TODO move this reassignment elsewhere so we don't need an
372 // ugly mutable member
373 pStartTime.emplace(loopOffset);
374 t0 = tracks.GetStartTime();
375 t1 = tracks.GetEndTime();
376 }
377 } else {
378 // move t0 to valid range
379 if (t0 < 0) {
380 t0 = tracks.GetStartTime();
381 }
382 else if (t0 > tracks.GetEndTime()) {
383 t0 = tracks.GetEndTime();
384 }
385#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
386 else {
387 initSeek = t0; //AC: initSeek is where playback will 'start'
388 if (!pStartTime)
389 pStartTime.emplace(initSeek);
390 t0 = tracks.GetStartTime();
391 }
392#endif
393 }
394 t1 = tracks.GetEndTime();
395 }
396 else {
397 // maybe t1 < t0, with backwards scrubbing for instance
398 if (backwards)
399 std::swap(t0, t1);
400
401 t0 = std::max(0.0, std::min(t0, latestEnd));
402 t1 = std::max(0.0, std::min(t1, latestEnd));
403
404 if (backwards)
405 std::swap(t0, t1);
406 }
407
408 int token = -1;
409
410 if (t1 != t0) {
411 if (cutpreview) {
412 const double tless = std::min(t0, t1);
413 const double tgreater = std::max(t0, t1);
414 double beforeLen, afterLen;
415 gPrefs->Read(wxT("/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
416 gPrefs->Read(wxT("/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
417 double tcp0 = tless-beforeLen;
418 const double diff = tgreater - tless;
419 double tcp1 = tgreater+afterLen;
420 if (backwards)
421 std::swap(tcp0, tcp1);
422 AudioIOStartStreamOptions myOptions = options;
423 myOptions.policyFactory =
424 [tless, diff](auto&) -> std::unique_ptr<PlaybackPolicy> {
425 return std::make_unique<CutPreviewPlaybackPolicy>(tless, diff);
426 };
427 token = gAudioIO->StartStream(
428 MakeTransportTracks(TrackList::Get(*p), false, nonWaveToo),
429 tcp0, tcp1, tcp1, myOptions);
430 }
431 else {
432 double mixerLimit = t1;
433 if (newDefault) {
434 mixerLimit = latestEnd;
435 if (pStartTime && *pStartTime >= t1)
436 t1 = latestEnd;
437 }
438 token = gAudioIO->StartStream(
439 MakeTransportTracks(tracks, false, nonWaveToo),
440 t0, t1, mixerLimit, options);
441 }
442 if (token != 0) {
443 success = true;
445 }
446 else {
447 // Bug1627 (part of it):
448 // infinite error spew when trying to start scrub:
449 // Problem was that the error dialog yields to events,
450 // causing recursion to this function in the scrub timer
451 // handler! Easy fix, just delay the user alert instead.
452 auto &window = GetProjectFrame( mProject );
453 window.CallAfter( [&]{
454 using namespace BasicUI;
455 // Show error message if stream could not be opened
457 XO("Error"),
458 XO("Error opening sound device.\nTry changing the audio host, playback device and the project sample rate."),
459 wxT("Error_opening_sound_device"),
460 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
461 });
462 }
463 }
464
465 if (!success)
466 return -1;
467
468 return token;
469}
470
471void ProjectAudioManager::PlayCurrentRegion(bool newDefault /* = false */,
472 bool cutpreview /* = false */)
473{
474 auto &projectAudioManager = *this;
475 bool canStop = projectAudioManager.CanStopAudioStream();
476
477 if ( !canStop )
478 return;
479
480 AudacityProject *p = &mProject;
481
482 {
483
484 const auto &playRegion = ViewInfo::Get( *p ).playRegion;
485
486 if (newDefault)
487 cutpreview = false;
488 auto options = ProjectAudioIO::GetDefaultOptions(*p, newDefault);
489 if (cutpreview)
490 options.envelope = nullptr;
491 auto mode =
492 cutpreview ? PlayMode::cutPreviewPlay
493 : newDefault ? PlayMode::loopedPlay
495 PlayPlayRegion(SelectedRegion(playRegion.GetStart(), playRegion.GetEnd()),
496 options,
497 mode);
498 }
499}
500
501void ProjectAudioManager::Stop(bool stopStream /* = true*/)
502{
503 AudacityProject *project = &mProject;
504 auto &projectAudioManager = *this;
505 bool canStop = projectAudioManager.CanStopAudioStream();
506
507 if ( !canStop )
508 return;
509
510 if(project) {
511 // Let scrubbing code do some appearance change
512 auto &scrubber = Scrubber::Get( *project );
513 scrubber.StopScrubbing();
514 }
515
516 auto gAudioIO = AudioIO::Get();
517
518 auto cleanup = finally( [&]{
519 projectAudioManager.SetStopping( false );
520 } );
521
522 if (stopStream && gAudioIO->IsBusy()) {
523 // flag that we are stopping
524 projectAudioManager.SetStopping( true );
525 // Allow UI to update for that
526 while( wxTheApp->ProcessIdle() )
527 ;
528 }
529
530 if(stopStream)
531 gAudioIO->StopStream();
532
533 projectAudioManager.SetLooping( false );
534 projectAudioManager.SetCutting( false );
535
536 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
537 gAudioIO->AILADisable();
538 #endif
539
540 projectAudioManager.SetPausedOff();
541 //Make sure you tell gAudioIO to unpause
542 gAudioIO->SetPaused( false );
543
544 // So that we continue monitoring after playing or recording.
545 // also clean the MeterQueues
546 if( project ) {
547 auto &projectAudioIO = ProjectAudioIO::Get( *project );
548 auto meter = projectAudioIO.GetPlaybackMeter();
549 if( meter ) {
550 meter->Clear();
551 }
552
553 meter = projectAudioIO.GetCaptureMeter();
554 if( meter ) {
555 meter->Clear();
556 }
557 }
558}
559
560
562 AudacityProject &proj, bool selectedOnly, double targetRate)
563{
564 auto p = &proj;
565 size_t recordingChannels = std::max(0, AudioIORecordChannels.Read());
566 bool strictRules = (recordingChannels <= 2);
567
568 // Iterate over all wave tracks, or over selected wave tracks only.
569 // If target rate was specified, ignore all tracks with other rates.
570 //
571 // In the usual cases of one or two recording channels, seek a first-fit
572 // unbroken sub-sequence for which the total number of channels matches the
573 // required number exactly. Never drop inputs or fill only some channels
574 // of a track.
575 //
576 // In case of more than two recording channels, choose tracks only among the
577 // selected. Simply take the earliest wave tracks, until the number of
578 // channels is enough. If there are fewer channels than inputs, but at least
579 // one channel, then some of the input channels will be dropped.
580 //
581 // Resulting tracks may be non-consecutive within the list of all tracks
582 // (there may be non-wave tracks between, or non-selected tracks when
583 // considering selected tracks only.)
584
585 if (!strictRules && !selectedOnly)
586 return {};
587
588 auto &trackList = TrackList::Get(*p);
589 WritableSampleTrackArray candidates;
590 std::vector<unsigned> channelCounts;
591 size_t totalChannels = 0;
592 const auto range = trackList.Any<WaveTrack>();
593 for (auto candidate : selectedOnly ? range + &Track::IsSelected : range) {
594 if (targetRate != RATE_NOT_SELECTED && candidate->GetRate() != targetRate)
595 continue;
596
597 // count channels in this track
598 const auto nChannels = candidate->NChannels();
599 if (strictRules && nChannels > recordingChannels) {
600 // The recording would under-fill this track's channels
601 // Can't use any partial accumulated results
602 // either. Keep looking.
603 candidates.clear();
604 channelCounts.clear();
605 totalChannels = 0;
606 continue;
607 }
608 else {
609 // Might use this but may have to discard some of the accumulated
610 while(strictRules &&
611 nChannels + totalChannels > recordingChannels) {
612 candidates.erase(candidates.begin());
613 auto nOldChannels = channelCounts[0];
614 assert(nOldChannels > 0);
615 channelCounts.erase(channelCounts.begin());
616 totalChannels -= nOldChannels;
617 }
618 candidates.push_back(candidate->SharedPointer<WaveTrack>());
619 channelCounts.push_back(nChannels);
620 totalChannels += nChannels;
621 if (totalChannels >= recordingChannels)
622 // Done!
623 return candidates;
624 }
625 }
626
627 if (!strictRules && !candidates.empty())
628 // good enough
629 return candidates;
630
631 // If the loop didn't exit early, we could not find enough channels
632 return {};
633}
634
636void ProjectAudioManager::OnRecord(bool altAppearance)
637{
638 bool bPreferNewTrack;
639 gPrefs->Read("/GUI/PreferNewTrackRecord", &bPreferNewTrack, false);
640 const bool appendRecord = (altAppearance == bPreferNewTrack);
641
642 // Code from CommandHandler start...
643 AudacityProject *p = &mProject;
644
645 if (p) {
646 const auto &selectedRegion = ViewInfo::Get( *p ).selectedRegion;
647 double t0 = selectedRegion.t0();
648 double t1 = selectedRegion.t1();
649 // When no time selection, recording duration is 'unlimited'.
650 if (t1 == t0)
651 t1 = DBL_MAX;
652
653 auto options = ProjectAudioIO::GetDefaultOptions(*p);
654 WritableSampleTrackArray existingTracks;
655
656 // Checking the selected tracks: counting them and
657 // making sure they all have the same rate
658 const auto selectedTracks{ GetPropertiesOfSelected(*p) };
659 const int rateOfSelected{ selectedTracks.rateOfSelected };
660 const bool anySelected{ selectedTracks.anySelected };
661 const bool allSameRate{ selectedTracks.allSameRate };
662
663 if (!allSameRate) {
664 AudacityMessageBox(XO("The tracks selected "
665 "for recording must all have the same sampling rate"),
666 XO("Mismatched Sampling Rates"),
667 wxICON_ERROR | wxCENTRE);
668
669 return;
670 }
671
672 if (appendRecord) {
673 // Try to find wave tracks to record into. (If any are selected,
674 // try to choose only from them; else if wave tracks exist, may record into any.)
675 existingTracks = ChooseExistingRecordingTracks(*p, true, rateOfSelected);
676 if (!existingTracks.empty()) {
677 t0 = std::max(t0,
679 .max(&Track::GetEndTime));
680 options.rate = rateOfSelected;
681 }
682 else {
683 if (anySelected && rateOfSelected != options.rate) {
685 "Too few tracks are selected for recording at this sample rate.\n"
686 "(Audacity requires two channels at the same sample rate for\n"
687 "each stereo track)"),
688 XO("Too Few Compatible Tracks Selected"),
689 wxICON_ERROR | wxCENTRE);
690
691 return;
692 }
693
694 existingTracks = ChooseExistingRecordingTracks(*p, false, options.rate);
695 if (!existingTracks.empty())
696 {
697 const auto endTime = accumulate(
698 existingTracks.begin(), existingTracks.end(),
699 std::numeric_limits<double>::lowest(),
700 [](double acc, auto &pTrack) {
701 return std::max(acc, pTrack->GetEndTime());
702 }
703 );
704
705 //If there is a suitable track, then adjust t0 so
706 //that recording not starts before the end of that track
707 t0 = std::max(t0, endTime);
708 }
709 // If suitable tracks still not found, will record into NEW ones,
710 // starting with t0
711 }
712
713 // Whether we decided on NEW tracks or not:
714 if (t1 <= selectedRegion.t0() && selectedRegion.t1() > selectedRegion.t0()) {
715 t1 = selectedRegion.t1(); // record within the selection
716 }
717 else {
718 t1 = DBL_MAX; // record for a long, long time
719 }
720 }
721
722 TransportSequences transportTracks;
723 if (UseDuplex()) {
724 // Remove recording tracks from the list of tracks for duplex ("overdub")
725 // playback.
726 /* TODO: set up stereo tracks if that is how the user has set up
727 * their preferences, and choose sample format based on prefs */
728 transportTracks =
729 MakeTransportTracks(TrackList::Get( *p ), false, true);
730 for (const auto &wt : existingTracks) {
731 auto end = transportTracks.playbackSequences.end();
732 auto it = std::find_if(
733 transportTracks.playbackSequences.begin(), end,
734 [&wt](const auto& playbackSequence) {
735 return playbackSequence->FindChannelGroup() ==
736 wt->FindChannelGroup();
737 });
738 if (it != end)
739 transportTracks.playbackSequences.erase(it);
740 }
741 }
742
743 std::copy(existingTracks.begin(), existingTracks.end(),
744 back_inserter(transportTracks.captureSequences));
745
746 DoRecord(*p, transportTracks, t0, t1, altAppearance, options);
747 }
748}
749
751{
752 bool duplex;
753 gPrefs->Read(wxT("/AudioIO/Duplex"), &duplex, true);
754 return duplex;
755}
756
758 const TransportSequences &sequences,
759 double t0, double t1,
760 bool altAppearance,
761 const AudioIOStartStreamOptions &options)
762{
763 auto &projectAudioManager = *this;
764
765 CommandFlag flags = AlwaysEnabledFlag; // 0 means recalc flags.
766
767 // NB: The call may have the side effect of changing flags.
769 flags,
771
772 if (!allowed)
773 return false;
774 // ...end of code from CommandHandler.
775
776 auto gAudioIO = AudioIO::Get();
777 if (gAudioIO->IsBusy())
778 return false;
779
780 projectAudioManager.SetAppending(!altAppearance);
781
782 bool success = false;
783
784 auto transportSequences = sequences;
785
786 // Will replace any given capture tracks with temporaries
787 transportSequences.captureSequences.clear();
788
789 const auto p = &project;
790 auto &trackList = TrackList::Get(project);
791
792 bool appendRecord = !sequences.captureSequences.empty();
793
794 auto insertEmptyInterval =
795 [&](WaveTrack &track, double t0, bool placeholder) {
796 wxString name;
797 for (auto i = 1; ; ++i) {
798 //i18n-hint a numerical suffix added to distinguish otherwise like-named clips when new record started
799 name = XC("%s #%d", "clip name template")
800 .Format(track.GetName(), i).Translation();
801 if (!track.HasClipNamed(name))
802 break;
803 }
804
805 auto clip = track.CreateClip(t0, name);
806 // So that the empty clip is not skipped for insertion:
807 clip->SetIsPlaceholder(true);
808 track.InsertInterval(clip, true);
809 if (!placeholder)
810 clip->SetIsPlaceholder(false);
811 return clip;
812 };
813
814 auto &pendingTracks = PendingTracks::Get(project);
815
816 {
817 if (appendRecord) {
818 // Append recording:
819 // Pad selected/all wave tracks to make them all the same length
820 for (const auto &sequence : sequences.captureSequences) {
821 WaveTrack *wt{};
822 if (!(wt = dynamic_cast<WaveTrack *>(sequence.get()))) {
823 assert(false);
824 continue;
825 }
826 auto endTime = wt->GetEndTime();
827
828 // If the track was chosen for recording and playback both,
829 // remember the original in preroll tracks, before making the
830 // pending replacement.
831 const auto shared = wt->SharedPointer<WaveTrack>();
832 // prerollSequences should be a subset of playbackSequences.
833 const auto &range = transportSequences.playbackSequences;
834 bool prerollTrack = any_of(range.begin(), range.end(),
835 [&](const auto &pSequence){
836 return shared.get() == pSequence->FindChannelGroup(); });
837 if (prerollTrack)
838 transportSequences.prerollSequences.push_back(shared);
839
840 // A function that copies all the non-sample data between
841 // wave tracks; in case the track recorded to changes scale
842 // type (for instance), during the recording.
843 auto updater = [](Track &d, const Track &s){
844 assert(d.NChannels() == s.NChannels());
845 auto &dst = static_cast<WaveTrack&>(d);
846 auto &src = static_cast<const WaveTrack&>(s);
847 dst.Init(src);
848 };
849
850 // End of current track is before or at recording start time.
851 // Less than or equal, not just less than, to ensure a clip boundary.
852 // when append recording.
853 //const auto pending = static_cast<WaveTrack*>(newTrack);
854 const auto lastClip = wt->GetRightmostClip();
855 // RoundedT0 to have a new clip created when punch-and-roll
856 // recording with the cursor in the second half of the space
857 // between two samples
858 // (https://github.com/audacity/audacity/issues/5113#issuecomment-1705154108)
859 const auto recordingStart =
860 std::round(t0 * wt->GetRate()) / wt->GetRate();
861 const auto recordingStartsBeforeTrackEnd =
862 lastClip && recordingStart < lastClip->GetPlayEndTime();
863 // Recording doesn't start before the beginning of the last clip
864 // - or the check for creating a new clip or not should be more
865 // general than that ...
866 assert(
867 !recordingStartsBeforeTrackEnd ||
868 lastClip->WithinPlayRegion(recordingStart));
870 if (!recordingStartsBeforeTrackEnd ||
871 lastClip->HasPitchOrSpeed())
872 newClip = insertEmptyInterval(*wt, t0, true);
873 // Get a copy of the track to be appended, to be pushed into
874 // undo history only later.
875 const auto pending = static_cast<WaveTrack*>(
876 pendingTracks.RegisterPendingChangedTrack(updater, wt)
877 );
878 // Source clip was marked as placeholder so that it would not be
879 // skipped in clip copying. Un-mark it and its copy now
880 if (newClip)
881 newClip->SetIsPlaceholder(false);
882 if (auto copiedClip = pending->NewestOrNewClip())
883 copiedClip->SetIsPlaceholder(false);
884 transportSequences.captureSequences
885 .push_back(pending->SharedPointer<WaveTrack>());
886 }
887 pendingTracks.UpdatePendingTracks();
888 }
889
890 if (transportSequences.captureSequences.empty()) {
891 // recording to NEW track(s).
892 bool recordingNameCustom, useTrackNumber, useDateStamp, useTimeStamp;
893 wxString defaultTrackName, defaultRecordingTrackName;
894
895 // Count the tracks.
896 auto numTracks = trackList.Any<const WaveTrack>().size();
897
898 auto recordingChannels = std::max(1, AudioIORecordChannels.Read());
899
900 gPrefs->Read(wxT("/GUI/TrackNames/RecordingNameCustom"), &recordingNameCustom, false);
901 gPrefs->Read(wxT("/GUI/TrackNames/TrackNumber"), &useTrackNumber, false);
902 gPrefs->Read(wxT("/GUI/TrackNames/DateStamp"), &useDateStamp, false);
903 gPrefs->Read(wxT("/GUI/TrackNames/TimeStamp"), &useTimeStamp, false);
904 defaultTrackName = trackList.MakeUniqueTrackName(WaveTrack::GetDefaultAudioTrackNamePreference());
905 gPrefs->Read(wxT("/GUI/TrackNames/RecodingTrackName"), &defaultRecordingTrackName, defaultTrackName);
906
907 wxString baseTrackName = recordingNameCustom? defaultRecordingTrackName : defaultTrackName;
908
909 auto newTracks =
910 WaveTrackFactory::Get(*p).CreateMany(recordingChannels);
911 const auto first = *newTracks->begin();
912 int trackCounter = 0;
913 const auto minimizeChannelView = recordingChannels > 2
915 for (auto newTrack : newTracks->Any<WaveTrack>()) {
916 // Quantize bounds to the rate of the new track.
917 if (newTrack == first) {
918 if (t0 < DBL_MAX)
919 t0 = newTrack->SnapToSample(t0);
920 if (t1 < DBL_MAX)
921 t1 = newTrack->SnapToSample(t1);
922 }
923
924 newTrack->MoveTo(t0);
925 wxString nameSuffix = wxString(wxT(""));
926
927 if (useTrackNumber) {
928 nameSuffix += wxString::Format(wxT("%d"), 1 + (int) numTracks + trackCounter++);
929 }
930
931 if (useDateStamp) {
932 if (!nameSuffix.empty()) {
933 nameSuffix += wxT("_");
934 }
935 nameSuffix += wxDateTime::Now().FormatISODate();
936 }
937
938 if (useTimeStamp) {
939 if (!nameSuffix.empty()) {
940 nameSuffix += wxT("_");
941 }
942 nameSuffix += wxDateTime::Now().FormatISOTime();
943 }
944
945 // ISO standard would be nice, but ":" is unsafe for file name.
946 nameSuffix.Replace(wxT(":"), wxT("-"));
947
948 if (baseTrackName.empty())
949 newTrack->SetName(nameSuffix);
950 else if (nameSuffix.empty())
951 newTrack->SetName(baseTrackName);
952 else
953 newTrack->SetName(baseTrackName + wxT("_") + nameSuffix);
954
955 //create a new clip with a proper name before recording is started
956 insertEmptyInterval(*newTrack, t0, false);
957
958 transportSequences.captureSequences.push_back(
959 std::static_pointer_cast<WaveTrack>(newTrack->shared_from_this())
960 );
961
962 for(auto channel : newTrack->Channels())
963 {
964 ChannelView::Get(*channel).SetMinimized(minimizeChannelView);
965 }
966 }
967 pendingTracks.RegisterPendingNewTracks(std::move(*newTracks));
968 // Bug 1548. First of new tracks needs the focus.
969 TrackFocus::Get(project).Set(first);
970 if (!trackList.empty())
971 Viewport::Get(project).ShowTrack(**trackList.rbegin());
972 }
973
974 //Automated Input Level Adjustment Initialization
975 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
976 gAudioIO->AILAInitialize();
977 #endif
978
979 int token =
980 gAudioIO->StartStream(transportSequences, t0, t1, t1, options);
981
982 success = (token != 0);
983
984 if (success) {
986 }
987 else {
988 CancelRecording();
989
990 // Show error message if stream could not be opened
991 auto msg = XO("Error opening recording device.\nError code: %s")
992 .Format( gAudioIO->LastPaErrorString() );
993 using namespace BasicUI;
995 XO("Error"), msg, wxT("Error_opening_sound_device"),
996 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
997 }
998 }
999
1000 return success;
1001}
1002
1004{
1005 auto &projectAudioManager = *this;
1006 bool canStop = projectAudioManager.CanStopAudioStream();
1007
1008 if ( !canStop ) {
1009 return;
1010 }
1011
1012 bool paused = !projectAudioManager.Paused();
1013 TogglePaused();
1014
1015 auto gAudioIO = AudioIO::Get();
1016
1017 auto project = &mProject;
1018 auto &scrubber = Scrubber::Get( *project );
1019
1020 // Bug 1494 - Pausing a seek or scrub should just STOP as
1021 // it is confusing to be in a paused scrub state.
1022 bool bStopInstead = paused &&
1024 !scrubber.IsSpeedPlaying() &&
1025 !scrubber.IsKeyboardScrubbing();
1026
1027 if (bStopInstead) {
1028 Stop();
1029 return;
1030 }
1031
1033 scrubber.Pause(paused);
1034 else
1035 {
1036 gAudioIO->SetPaused(paused);
1037 }
1038}
1039
1040
1042{
1043 mPaused.fetch_xor(1, std::memory_order::memory_order_relaxed);
1044}
1045
1047{
1048 mPaused.store(0, std::memory_order::memory_order_relaxed);
1049}
1050
1052{
1053 return mPaused.load(std::memory_order_relaxed) == 1;
1054}
1055
1056
1058{
1059 const auto project = &mProject;
1061}
1062
1064{
1065 auto &project = mProject;
1066
1067 mDisplayedRate = rate;
1068
1069 auto display = FormatRate( rate );
1070
1072}
1073
1075{
1076 // Auto-save was done here before, but it is unnecessary, provided there
1077 // are sufficient autosaves when pushing or modifying undo states.
1078}
1079
1080// This is called after recording has stopped and all tracks have flushed.
1082{
1083 auto &project = mProject;
1084 auto &projectAudioIO = ProjectAudioIO::Get( project );
1085 auto &projectFileIO = ProjectFileIO::Get( project );
1086
1087 // Only push state if we were capturing and not monitoring
1088 if (projectAudioIO.GetAudioIOToken() > 0)
1089 {
1090 auto &history = ProjectHistory::Get( project );
1091
1092 if (IsTimerRecordCancelled()) {
1093 // discard recording
1094 history.RollbackState();
1095 // Reset timer record
1096 ResetTimerRecordCancelled();
1097 }
1098 else {
1099 // Add to history
1100 // We want this to have No-fail-guarantee if we get here from exception
1101 // handling of recording, and that means we rely on the last autosave
1102 // successfully committed to the database, not risking a failure
1103 auto flags = AudioIO::Get()->HasRecordingException()
1106 history.PushState(XO("Recorded Audio"), XO("Record"), flags);
1107
1108 // Now, we may add a label track to give information about
1109 // dropouts. We allow failure of this.
1110 auto gAudioIO = AudioIO::Get();
1111 auto &intervals = gAudioIO->LostCaptureIntervals();
1112 if (intervals.size())
1113 Publish( RecordingDropoutEvent{ intervals } );
1114 }
1115 }
1116}
1117
1119{
1120 auto &project = mProject;
1121 auto &projectFileIO = ProjectFileIO::Get( project );
1122
1123 wxTheApp->CallAfter( [&]{ projectFileIO.AutoSave(true); });
1124}
1125
1127{
1128 const auto project = &mProject;
1130}
1131
1133{
1134 auto& project = mProject;
1135 auto gAudioIO = AudioIO::Get();
1136 if (gAudioIO && &project == gAudioIO->GetOwningProject().get())
1137 {
1138 bool canStop = CanStopAudioStream();
1139
1140 gAudioIO->SetPaused(!gAudioIO->IsPaused());
1141
1142 if (canStop)
1143 {
1144 // Instead of calling ::OnPause here, we can simply do the only thing it does (i.e. toggling the pause state),
1145 // because scrubbing can not happen while recording
1146 TogglePaused();
1147 }
1148 }
1149}
1150
1152{
1154 Stop();
1155}
1156
1158{
1159 auto gAudioIO = AudioIO::Get();
1160 return
1161 gAudioIO->IsBusy() &&
1162 CanStopAudioStream() &&
1163 // ... and not merely monitoring
1164 !gAudioIO->IsMonitoring() &&
1165 // ... and not punch-and-roll recording
1166 gAudioIO->GetNumCaptureChannels() == 0;
1167}
1168
1170{
1171 auto gAudioIO = AudioIO::Get();
1172 return
1173 gAudioIO->IsBusy() &&
1174 CanStopAudioStream() &&
1175 gAudioIO->GetNumCaptureChannels() > 0;
1176}
1177
1179{
1180 auto gAudioIO = AudioIO::Get();
1181 return (!gAudioIO->IsStreamActive() ||
1182 gAudioIO->IsMonitoring() ||
1183 gAudioIO->GetOwningProject().get() == &mProject );
1184}
1185
1188 [](const AudacityProject &project){
1189 auto &projectAudioManager = ProjectAudioManager::Get( project );
1190 bool canStop = projectAudioManager.CanStopAudioStream();
1191 return canStop;
1192 }
1193 }; return flag; }
1194
1197[](AudacityProject &project, bool newDefault) -> AudioIOStartStreamOptions {
1199 auto options = ProjectAudioIO::DefaultOptionsFactory(project, newDefault);
1200
1202 options.listener = ProjectAudioManager::Get(project).shared_from_this();
1203
1204 bool loopEnabled = ViewInfo::Get(project).playRegion.Active();
1205 options.loopEnabled = loopEnabled;
1206
1207 if (newDefault) {
1208 const double trackEndTime = TrackList::Get(project).GetEndTime();
1209 const double loopEndTime = ViewInfo::Get(project).playRegion.GetEnd();
1210 options.policyFactory = [&project, trackEndTime, loopEndTime](
1211 const AudioIOStartStreamOptions &options)
1212 -> std::unique_ptr<PlaybackPolicy>
1213 {
1214 return std::make_unique<DefaultPlaybackPolicy>( project,
1215 trackEndTime, loopEndTime, options.pStartTime,
1216 options.loopEnabled, options.variableSpeed);
1217 };
1218
1219 // Start play from left edge of selection
1220 options.pStartTime.emplace(ViewInfo::Get(project).selectedRegion.t0());
1221 }
1222
1223 return options;
1224} };
1225
1228{
1230 auto gAudioIO = AudioIO::Get();
1231 auto PlayAtSpeedRate = gAudioIO->GetBestRate(
1232 false, //not capturing
1233 true, //is playing
1234 ProjectRate::Get( project ).GetRate() //suggested rate
1235 );
1236 result.rate = PlayAtSpeedRate;
1237 return result;
1238}
1239
1240// Stop playing or recording, if paused.
1242{
1243 if( AudioIOBase::Get()->IsPaused() )
1244 Stop();
1245}
1246
1247bool ProjectAudioManager::DoPlayStopSelect( bool click, bool shift )
1248{
1249 auto &project = mProject;
1250 auto &scrubber = Scrubber::Get( project );
1252 auto &viewInfo = ViewInfo::Get( project );
1253 auto &selection = viewInfo.selectedRegion;
1254 auto gAudioIO = AudioIO::Get();
1255
1256 //If busy, stop playing, make sure everything is unpaused.
1257 if (scrubber.HasMark() ||
1258 gAudioIO->IsStreamActive(token)) {
1259 // change the selection
1260 auto time = gAudioIO->GetStreamTime();
1261 // Test WasSpeedPlaying(), not IsSpeedPlaying()
1262 // as we could be stopped now. Similarly WasKeyboardScrubbing().
1263 if (click && (scrubber.WasSpeedPlaying() || scrubber.WasKeyboardScrubbing()))
1264 {
1265 ;// don't change the selection.
1266 }
1267 else if (shift && click) {
1268 // Change the region selection, as if by shift-click at the play head
1269 auto t0 = selection.t0(), t1 = selection.t1();
1270 if (time < t0)
1271 // Grow selection
1272 t0 = time;
1273 else if (time > t1)
1274 // Grow selection
1275 t1 = time;
1276 else {
1277 // Shrink selection, changing the nearer boundary
1278 if (fabs(t0 - time) < fabs(t1 - time))
1279 t0 = time;
1280 else
1281 t1 = time;
1282 }
1283 selection.setTimes(t0, t1);
1284 }
1285 else if (click){
1286 // avoid a point at negative time.
1287 time = wxMax( time, 0 );
1288 // Set a point selection, as if by a click at the play head
1289 selection.setTimes(time, time);
1290 } else
1291 // How stop and set cursor always worked
1292 // -- change t0, collapsing to point only if t1 was greater
1293 selection.setT0(time, false);
1294
1295 ProjectHistory::Get( project ).ModifyState(false); // without bWantsAutoSave
1296 return true;
1297 }
1298 return false;
1299}
1300
1301// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and
1302// "OnStopSelect" merged.
1304{
1305 auto gAudioIO = AudioIO::Get();
1306 if (DoPlayStopSelect(false, false))
1307 Stop();
1308 else if (!gAudioIO->IsBusy()) {
1309 //Otherwise, start playing (assuming audio I/O isn't busy)
1310
1311 // Will automatically set mLastPlayMode
1312 PlayCurrentRegion(false);
1313 }
1314}
1315
1317 []{ return PausedFlag(); },
1318 []{ return AudioIONotBusyFlag(); },
1319 []( const AudacityProject &project ){
1324 }
1325}};
1326
1327// GetSelectedProperties collects information about
1328// currently selected audio tracks
1331{
1332 double rateOfSelection{ RATE_NOT_SELECTED };
1333
1334 PropertiesOfSelected result;
1335 result.allSameRate = true;
1336
1337 const auto selectedTracks{
1338 TrackList::Get(proj).Selected<const WaveTrack>() };
1339
1340 for (const auto & track : selectedTracks) {
1341 if (rateOfSelection != RATE_NOT_SELECTED &&
1342 track->GetRate() != rateOfSelection)
1343 result.allSameRate = false;
1344 else if (rateOfSelection == RATE_NOT_SELECTED)
1345 rateOfSelection = track->GetRate();
1346 }
1347
1348 result.anySelected = !selectedTracks.empty();
1349 result.rateOfSelected = rateOfSelection;
1350
1351 return result;
1352}
wxT("CloseDown"))
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:34
std::bitset< NCommandFlags > CommandFlag
Definition: CommandFlag.h:30
const ReservedCommandFlag & AudioIONotBusyFlag()
const ReservedCommandFlag & PausedFlag()
int min(int a, int b)
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
#define field(n, t)
Definition: ImportAUP.cpp:165
#define XC(s, c)
Definition: Internat.h:37
constexpr size_t TimeQueueGrainSize
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
Definition: Project.cpp:129
static TranslatableString FormatRate(int rate)
static ProjectAudioIO::DefaultOptions::Scope sScope
Install an implementation in a library hook.
AudioIOStartStreamOptions DefaultSpeedPlayOptions(AudacityProject &project)
static RegisteredMenuItemEnabler stopIfPaused
static AudacityProject::AttachedObjects::RegisteredFactory sProjectAudioManagerKey
const ReservedCommandFlag & CanStopAudioStreamFlag()
PropertiesOfSelected GetPropertiesOfSelected(const AudacityProject &proj)
@ cutPreviewPlay
std::vector< std::shared_ptr< WritableSampleTrack > > WritableSampleTrackArray
constexpr int RATE_NOT_SELECTED
ProjectFileIOMessage
Subscribe to ProjectFileIO to receive messages; always in idle time.
Definition: ProjectFileIO.h:50
@ CheckpointFailure
Failure happened in a worker thread.
an object holding per-project preferred sample rate
StatusBarField RateStatusBarField()
ID of the third field in the status bar. This field is used to display the current rate.
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
const auto tracks
const auto project
TransportSequences MakeTransportTracks(TrackList &trackList, bool selectedOnly, bool nonWaveToo)
static CustomUpdaterValue updater
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:90
static AudioIOBase * Get()
Definition: AudioIOBase.cpp:94
static AudioIO * Get()
Definition: AudioIO.cpp:126
bool HasRecordingException() const
Definition: AudioIO.h:374
double GetEndTime() const
Get the maximum of End() values of intervals, or 0 when none.
Definition: Channel.cpp:61
virtual size_t NChannels() const =0
Report the number of channels.
static ChannelView & Get(Channel &channel)
void SetMinimized(bool minimized)
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
Definition: ClientData.h:275
bool TryToMakeActionAllowed(CommandFlag &flags, CommandFlag flagsRqd)
static CommandManager & Get(AudacityProject &project)
typename GlobalVariable< DefaultOptions, const std::function< std::remove_pointer_t< decltype(DefaultFunction)> >, Default, Options... >::Scope Scope
An explicitly nonlocalized string, not meant for the user to see.
Definition: Identifier.h:22
double t0() const
Definition: ViewInfo.h:35
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
Definition: Observer.h:199
static PendingTracks & Get(AudacityProject &project)
void ClearPendingTracks(std::vector< std::shared_ptr< Track > > *pAdded=nullptr)
Forget pending track additions and changes;.
bool ApplyPendingTracks()
Change the state of the project.
double GetEnd() const
Definition: ViewInfo.h:135
bool Active() const
Definition: ViewInfo.h:124
AudioTrack subclass that can also be audibly replayed by the program.
Definition: PlayableTrack.h:40
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.
static AudioIOStartStreamOptions DefaultOptionsFactory(AudacityProject &project, bool newDefaults)
Default factory function ignores the second argument.
int GetAudioIOToken() const
void SetAudioIOToken(int token)
static AudioIOStartStreamOptions GetDefaultOptions(AudacityProject &project, bool newDefaults=false)
Invoke the global hook, supplying a default argument.
static ProjectAudioIO & Get(AudacityProject &project)
void Stop(bool stopStream=true)
static std::pair< TranslatableStrings, unsigned > StatusWidthFunction(const AudacityProject &project, StatusBarField field)
void OnAudioIONewBlocks() override
void OnAudioIOStartRecording() override
void OnCommitRecording() override
static WritableSampleTrackArray ChooseExistingRecordingTracks(AudacityProject &proj, bool selectedOnly, double targetRate=RATE_NOT_SELECTED)
Find suitable tracks to record into, or return an empty array.
void OnAudioIORate(int rate) override
static ProjectAudioManager & Get(AudacityProject &project)
void OnCheckpointFailure(ProjectFileIOMessage)
void OnAudioIOStopRecording() override
~ProjectAudioManager() override
ProjectAudioManager(AudacityProject &project)
int PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode playMode, bool backwards=false)
bool DoRecord(AudacityProject &project, const TransportSequences &transportSequences, double t0, double t1, bool altAppearance, const AudioIOStartStreamOptions &options)
Observer::Subscription mCheckpointFailureSubscription
void PlayCurrentRegion(bool newDefault=false, bool cutpreview=false)
void OnRecord(bool altAppearance)
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
std::pair< std::vector< TranslatableString >, unsigned > StatusWidthResult
static ProjectStatus & Get(AudacityProject &project)
void Set(const TranslatableString &msg, StatusBarField field=MainStatusBarField())
static Scrubber & Get(AudacityProject &project)
Definition: Scrubbing.cpp:186
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:207
Track * Get()
Definition: TrackFocus.cpp:156
Abstract base class for an object holding data associated with points on a time axis.
Definition: Track.h:110
bool IsSelected() const
Definition: Track.cpp:258
const wxString & GetName() const
Name is always the same for all channels of a group.
Definition: Track.cpp:64
bool Any() const
Definition: Track.cpp:255
double GetEndTime() const
Return the greatest end time of the tracks, or 0 when no tracks.
Definition: Track.cpp:784
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:314
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:967
static BoolSetting TracksFitVerticallyZoomed
Definition: TracksPrefs.h:30
Holds a msgid for the translation catalog; may also bind format arguments.
PlayRegion playRegion
Definition: ViewInfo.h:216
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:215
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
void ShowTrack(const Track &track)
Definition: Viewport.cpp:456
static Viewport & Get(AudacityProject &project)
Definition: Viewport.cpp:33
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:3349
TrackListHolder CreateMany(size_t nChannels)
Creates tracks with project's default rate and format and the given number of channels.
Definition: WaveTrack.cpp:425
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
bool HasClipNamed(const wxString &name) const
Definition: WaveTrack.cpp:711
IntervalHolder CreateClip(double offset=.0, const wxString &name=wxEmptyString, const Interval *pToCopy=nullptr, bool copyCutlines=true)
Definition: WaveTrack.cpp:2934
void InsertInterval(const IntervalHolder &interval, bool newClip, bool allowEmpty=false)
Definition: WaveTrack.cpp:3208
static wxString GetDefaultAudioTrackNamePreference()
Definition: WaveTrack.cpp:375
void Init(const WaveTrack &orig)
Definition: WaveTrack.cpp:555
std::shared_ptr< Interval > IntervalHolder
Definition: WaveTrack.h:209
size_t NChannels() const override
A constant property.
Definition: WaveTrack.cpp:532
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 OffsetSequenceTime(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.GetSequenceTime() 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.
virtual bool Read(const wxString &key, bool *value) const =0
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:264
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
Definition: NoteTrack.cpp:628
double GetRate(const Track &track)
Definition: TimeTrack.cpp:182
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
fastfloat_really_inline void round(adjusted_mantissa &am, callback cb) noexcept
Definition: fast_float.h:2512
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
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:73
std::optional< double > pStartTime
Definition: AudioIOBase.h:58
Options for variations of error dialogs; the default is for modal dialogs.
Definition: BasicUI.h:52
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 GetSequenceTime() const
Get current track time value, unadjusted.
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, after recording has stopped, when dropouts have been detected.
static bool IsScrubbing()
Definition: ScrubState.cpp:482
RecordableSequences captureSequences
Definition: AudioIO.h:71
ConstPlayableSequences playbackSequences
Definition: AudioIO.h:70