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 BasicUI::CallAfter([pProject = project.weak_from_this()]{
972 if (!pProject.expired()) {
973 auto &project = *pProject.lock();
974 auto &trackList = TrackList::Get(project);
975 Viewport::Get(project).ShowTrack(**trackList.rbegin());
976 }
977 });
978 }
979 }
980
981 //Automated Input Level Adjustment Initialization
982 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
983 gAudioIO->AILAInitialize();
984 #endif
985
986 int token =
987 gAudioIO->StartStream(transportSequences, t0, t1, t1, options);
988
989 success = (token != 0);
990
991 if (success) {
993 }
994 else {
995 CancelRecording();
996
997 // Show error message if stream could not be opened
998 auto msg = XO("Error opening recording device.\nError code: %s")
999 .Format( gAudioIO->LastPaErrorString() );
1000 using namespace BasicUI;
1002 XO("Error"), msg, wxT("Error_opening_sound_device"),
1003 ErrorDialogOptions{ ErrorDialogType::ModalErrorReport } );
1004 }
1005 }
1006
1007 return success;
1008}
1009
1011{
1012 auto &projectAudioManager = *this;
1013 bool canStop = projectAudioManager.CanStopAudioStream();
1014
1015 if ( !canStop ) {
1016 return;
1017 }
1018
1019 bool paused = !projectAudioManager.Paused();
1020 TogglePaused();
1021
1022 auto gAudioIO = AudioIO::Get();
1023
1024 auto project = &mProject;
1025 auto &scrubber = Scrubber::Get( *project );
1026
1027 // Bug 1494 - Pausing a seek or scrub should just STOP as
1028 // it is confusing to be in a paused scrub state.
1029 bool bStopInstead = paused &&
1031 !scrubber.IsSpeedPlaying() &&
1032 !scrubber.IsKeyboardScrubbing();
1033
1034 if (bStopInstead) {
1035 Stop();
1036 return;
1037 }
1038
1040 scrubber.Pause(paused);
1041 else
1042 {
1043 constexpr auto publish = true;
1044 gAudioIO->SetPaused(paused, publish);
1045 }
1046}
1047
1048
1050{
1051 mPaused.fetch_xor(1, std::memory_order::memory_order_relaxed);
1052}
1053
1055{
1056 mPaused.store(0, std::memory_order::memory_order_relaxed);
1057}
1058
1060{
1061 return mPaused.load(std::memory_order_relaxed) == 1;
1062}
1063
1064
1066{
1067 const auto project = &mProject;
1069}
1070
1072{
1073 auto &project = mProject;
1074
1075 mDisplayedRate = rate;
1076
1077 auto display = FormatRate( rate );
1078
1080}
1081
1083{
1084 // Auto-save was done here before, but it is unnecessary, provided there
1085 // are sufficient autosaves when pushing or modifying undo states.
1086}
1087
1088// This is called after recording has stopped and all tracks have flushed.
1090{
1091 auto &project = mProject;
1092 auto &projectAudioIO = ProjectAudioIO::Get( project );
1093 auto &projectFileIO = ProjectFileIO::Get( project );
1094
1095 // Only push state if we were capturing and not monitoring
1096 if (projectAudioIO.GetAudioIOToken() > 0)
1097 {
1098 auto &history = ProjectHistory::Get( project );
1099
1100 if (IsTimerRecordCancelled()) {
1101 // discard recording
1102 history.RollbackState();
1103 // Reset timer record
1104 ResetTimerRecordCancelled();
1105 }
1106 else {
1107 // Add to history
1108 // We want this to have No-fail-guarantee if we get here from exception
1109 // handling of recording, and that means we rely on the last autosave
1110 // successfully committed to the database, not risking a failure
1111 auto flags = AudioIO::Get()->HasRecordingException()
1114 history.PushState(XO("Recorded Audio"), XO("Record"), flags);
1115
1116 // Now, we may add a label track to give information about
1117 // dropouts. We allow failure of this.
1118 auto gAudioIO = AudioIO::Get();
1119 auto &intervals = gAudioIO->LostCaptureIntervals();
1120 if (intervals.size())
1121 Publish( RecordingDropoutEvent{ intervals } );
1122 }
1123 }
1124}
1125
1127{
1128 auto &project = mProject;
1129 auto &projectFileIO = ProjectFileIO::Get( project );
1130
1131 wxTheApp->CallAfter( [&]{ projectFileIO.AutoSave(true); });
1132}
1133
1135{
1136 const auto project = &mProject;
1138}
1139
1141{
1142 auto& project = mProject;
1143 auto gAudioIO = AudioIO::Get();
1144 if (gAudioIO && &project == gAudioIO->GetOwningProject().get())
1145 {
1146 bool canStop = CanStopAudioStream();
1147
1148 gAudioIO->SetPaused(!gAudioIO->IsPaused());
1149
1150 if (canStop)
1151 {
1152 // Instead of calling ::OnPause here, we can simply do the only thing it does (i.e. toggling the pause state),
1153 // because scrubbing can not happen while recording
1154 TogglePaused();
1155 }
1156 }
1157}
1158
1160{
1162 Stop();
1163}
1164
1166{
1167 auto gAudioIO = AudioIO::Get();
1168 return
1169 gAudioIO->IsBusy() &&
1170 CanStopAudioStream() &&
1171 // ... and not merely monitoring
1172 !gAudioIO->IsMonitoring() &&
1173 // ... and not punch-and-roll recording
1174 gAudioIO->GetNumCaptureChannels() == 0;
1175}
1176
1178{
1179 auto gAudioIO = AudioIO::Get();
1180 return
1181 gAudioIO->IsBusy() &&
1182 CanStopAudioStream() &&
1183 gAudioIO->GetNumCaptureChannels() > 0;
1184}
1185
1187{
1188 auto gAudioIO = AudioIO::Get();
1189 return (!gAudioIO->IsStreamActive() ||
1190 gAudioIO->IsMonitoring() ||
1191 gAudioIO->GetOwningProject().get() == &mProject );
1192}
1193
1196 [](const AudacityProject &project){
1197 auto &projectAudioManager = ProjectAudioManager::Get( project );
1198 bool canStop = projectAudioManager.CanStopAudioStream();
1199 return canStop;
1200 }
1201 }; return flag; }
1202
1205[](AudacityProject &project, bool newDefault) -> AudioIOStartStreamOptions {
1207 auto options = ProjectAudioIO::DefaultOptionsFactory(project, newDefault);
1208
1210 options.listener = ProjectAudioManager::Get(project).shared_from_this();
1211
1212 bool loopEnabled = ViewInfo::Get(project).playRegion.Active();
1213 options.loopEnabled = loopEnabled;
1214
1215 if (newDefault) {
1216 const double trackEndTime = TrackList::Get(project).GetEndTime();
1217 const double loopEndTime = ViewInfo::Get(project).playRegion.GetEnd();
1218 options.policyFactory = [&project, trackEndTime, loopEndTime](
1219 const AudioIOStartStreamOptions &options)
1220 -> std::unique_ptr<PlaybackPolicy>
1221 {
1222 return std::make_unique<DefaultPlaybackPolicy>( project,
1223 trackEndTime, loopEndTime, options.pStartTime,
1224 options.loopEnabled, options.variableSpeed);
1225 };
1226
1227 // Start play from left edge of selection
1228 options.pStartTime.emplace(ViewInfo::Get(project).selectedRegion.t0());
1229 }
1230
1231 return options;
1232} };
1233
1236{
1238 auto gAudioIO = AudioIO::Get();
1239 auto PlayAtSpeedRate = gAudioIO->GetBestRate(
1240 false, //not capturing
1241 true, //is playing
1242 ProjectRate::Get( project ).GetRate() //suggested rate
1243 );
1244 result.rate = PlayAtSpeedRate;
1245 return result;
1246}
1247
1248// Stop playing or recording, if paused.
1250{
1251 if( AudioIOBase::Get()->IsPaused() )
1252 Stop();
1253}
1254
1255bool ProjectAudioManager::DoPlayStopSelect( bool click, bool shift )
1256{
1257 auto &project = mProject;
1258 auto &scrubber = Scrubber::Get( project );
1260 auto &viewInfo = ViewInfo::Get( project );
1261 auto &selection = viewInfo.selectedRegion;
1262 auto gAudioIO = AudioIO::Get();
1263
1264 //If busy, stop playing, make sure everything is unpaused.
1265 if (scrubber.HasMark() ||
1266 gAudioIO->IsStreamActive(token)) {
1267 // change the selection
1268 auto time = gAudioIO->GetStreamTime();
1269 // Test WasSpeedPlaying(), not IsSpeedPlaying()
1270 // as we could be stopped now. Similarly WasKeyboardScrubbing().
1271 if (click && (scrubber.WasSpeedPlaying() || scrubber.WasKeyboardScrubbing()))
1272 {
1273 ;// don't change the selection.
1274 }
1275 else if (shift && click) {
1276 // Change the region selection, as if by shift-click at the play head
1277 auto t0 = selection.t0(), t1 = selection.t1();
1278 if (time < t0)
1279 // Grow selection
1280 t0 = time;
1281 else if (time > t1)
1282 // Grow selection
1283 t1 = time;
1284 else {
1285 // Shrink selection, changing the nearer boundary
1286 if (fabs(t0 - time) < fabs(t1 - time))
1287 t0 = time;
1288 else
1289 t1 = time;
1290 }
1291 selection.setTimes(t0, t1);
1292 }
1293 else if (click){
1294 // avoid a point at negative time.
1295 time = wxMax( time, 0 );
1296 // Set a point selection, as if by a click at the play head
1297 selection.setTimes(time, time);
1298 } else
1299 // How stop and set cursor always worked
1300 // -- change t0, collapsing to point only if t1 was greater
1301 selection.setT0(time, false);
1302
1303 ProjectHistory::Get( project ).ModifyState(false); // without bWantsAutoSave
1304 return true;
1305 }
1306 return false;
1307}
1308
1309// The code for "OnPlayStopSelect" is simply the code of "OnPlayStop" and
1310// "OnStopSelect" merged.
1312{
1313 auto gAudioIO = AudioIO::Get();
1314 if (DoPlayStopSelect(false, false))
1315 Stop();
1316 else if (!gAudioIO->IsBusy()) {
1317 //Otherwise, start playing (assuming audio I/O isn't busy)
1318
1319 // Will automatically set mLastPlayMode
1320 PlayCurrentRegion(false);
1321 }
1322}
1323
1325 []{ return PausedFlag(); },
1326 []{ return AudioIONotBusyFlag(); },
1327 []( const AudacityProject &project ){
1332 }
1333}};
1334
1335// GetSelectedProperties collects information about
1336// currently selected audio tracks
1339{
1340 double rateOfSelection{ RATE_NOT_SELECTED };
1341
1342 PropertiesOfSelected result;
1343 result.allSameRate = true;
1344
1345 const auto selectedTracks{
1346 TrackList::Get(proj).Selected<const WaveTrack>() };
1347
1348 for (const auto & track : selectedTracks) {
1349 if (rateOfSelection != RATE_NOT_SELECTED &&
1350 track->GetRate() != rateOfSelection)
1351 result.allSameRate = false;
1352 else if (rateOfSelection == RATE_NOT_SELECTED)
1353 rateOfSelection = track->GetRate();
1354 }
1355
1356 result.anySelected = !selectedTracks.empty();
1357 result.rateOfSelected = rateOfSelection;
1358
1359 return result;
1360}
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:365
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:136
bool Active() const
Definition: ViewInfo.h:125
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:217
NotifyingSelectedRegion selectedRegion
Definition: ViewInfo.h:216
static ViewInfo & Get(AudacityProject &project)
Definition: ViewInfo.cpp:235
static WaveTrackFactory & Get(AudacityProject &project)
Definition: WaveTrack.cpp:3360
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:2945
void InsertInterval(const IntervalHolder &interval, bool newClip, bool allowEmpty=false)
Definition: WaveTrack.cpp:3219
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 CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
Definition: BasicUI.cpp:214
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:272
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:72
ConstPlayableSequences playbackSequences
Definition: AudioIO.h:71