16#include <wx/statusbr.h>
51 return std::make_shared< ProjectAudioManager >( project );
72 project.Bind( EVT_CHECKPOINT_FAILURE,
81 return XO(
"Actual Rate: %d").Format( rate );
94 int rate = audioManager.mDisplayedRate;
123 double trackTime,
size_t nSamples)
override;
125 bool RepositionPlayback(
127 size_t frames,
size_t available)
override;
131 {
return mReversed ? mGapLeft + mGapLength : mGapLeft; }
133 {
return mReversed ? mGapLeft : mGapLeft + mGapLength; }
135 {
return mReversed ? trackTime1 >= trackTime2 : trackTime1 <= trackTime2; }
141 double mStart = 0, mEnd = 0;
144 double mDuration1 = 0, mDuration2 = 0;
145 double mInitDuration1 = 0, mInitDuration2 = 0;
147 bool mDiscontinuity{
false };
148 bool mReversed{
false };
151CutPreviewPlaybackPolicy::CutPreviewPlaybackPolicy(
152 double gapLeft,
double gapLength)
153: mGapLeft{ gapLeft }, mGapLength{ gapLength }
155 wxASSERT(gapLength >= 0.0);
167 double right =
mEnd = schedule.
mT1;
200 auto space = std::clamp(
mGapLeft - time, 0.0, offset);
234 size_t frames = available;
235 size_t toProduce = frames;
237 if (samples1 > 0 && samples1 < frames)
239 toProduce = frames = samples1.
as_size_t();
240 else if (samples1 == 0) {
242 if (samples2 < frames) {
249 return { available, frames, toProduce };
255 auto realDuration = nSamples /
mRate;
271 return {
mEnd, std::numeric_limits<double>::infinity()};
277 const Mixers &playbackMixers,
size_t,
size_t )
282 for (
auto &pMixer : playbackMixers)
283 pMixer->Reposition(newTime,
true);
296 auto &projectAudioManager = *
this;
297 bool canStop = projectAudioManager.CanStopAudioStream();
309 double t0 = selectedRegion.
t0();
310 double t1 = selectedRegion.
t1();
321 bool success =
false;
324 if (gAudioIO->IsBusy())
328 if (cutpreview && t0==t1)
335 mLastPlayMode = mode;
341 hasaudio = ! tracks.Any<
WaveTrack>().empty();
343 double latestEnd = tracks.GetEndTime();
348#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
349 double initSeek = 0.0;
351 double loopOffset = 0.0;
360 if ((t0 > selectedRegion.
t0()) && (t0 < selectedRegion.
t1())) {
361 t0 = selectedRegion.
t0();
362 t1 = selectedRegion.
t1();
367 loopOffset = t0 - tracks.GetStartTime();
371 pStartTime.emplace(loopOffset);
372 t0 = tracks.GetStartTime();
373 t1 = tracks.GetEndTime();
378 t0 = tracks.GetStartTime();
380 else if (t0 > tracks.GetEndTime()) {
381 t0 = tracks.GetEndTime();
383#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
387 pStartTime.emplace(initSeek);
388 t0 = tracks.GetStartTime();
392 t1 = tracks.GetEndTime();
399 t0 = std::max(0.0,
std::min(t0, latestEnd));
400 t1 = std::max(0.0,
std::min(t1, latestEnd));
410 const double tless =
std::min(t0, t1);
411 const double tgreater = std::max(t0, t1);
412 double beforeLen, afterLen;
413 gPrefs->Read(wxT(
"/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
414 gPrefs->Read(wxT(
"/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
415 double tcp0 = tless-beforeLen;
416 const double diff = tgreater - tless;
417 double tcp1 = tgreater+afterLen;
422 [tless, diff](
auto&) -> std::unique_ptr<PlaybackPolicy> {
423 return std::make_unique<CutPreviewPlaybackPolicy>(tless, diff);
425 token = gAudioIO->StartStream(
427 tcp0, tcp1, tcp1, myOptions);
430 double mixerLimit = t1;
432 mixerLimit = latestEnd;
433 if (pStartTime && *pStartTime >= t1)
436 token = gAudioIO->StartStream(
437 GetAllPlaybackTracks( tracks,
false, nonWaveToo ),
438 t0, t1, mixerLimit, options);
451 window.CallAfter( [&]{
456 XO(
"Error opening sound device.\nTry changing the audio host, playback device and the project sample rate."),
457 wxT(
"Error_opening_sound_device"),
472 auto &projectAudioManager = *
this;
473 bool canStop = projectAudioManager.CanStopAudioStream();
488 options.envelope =
nullptr;
493 PlayPlayRegion(
SelectedRegion(playRegion.GetStart(), playRegion.GetEnd()),
502 auto &projectAudioManager = *
this;
503 bool canStop = projectAudioManager.CanStopAudioStream();
511 scrubber.StopScrubbing();
516 auto cleanup =
finally( [&]{
517 projectAudioManager.SetStopping(
false );
520 if (stopStream && gAudioIO->IsBusy()) {
522 projectAudioManager.SetStopping(
true );
524 while( wxTheApp->ProcessIdle() )
529 gAudioIO->StopStream();
531 projectAudioManager.SetLooping(
false );
532 projectAudioManager.SetCutting(
false );
534 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
535 gAudioIO->AILADisable();
538 projectAudioManager.SetPausedOff();
540 gAudioIO->SetPaused(
false );
546 auto meter = projectAudioIO.GetPlaybackMeter();
551 meter = projectAudioIO.GetCaptureMeter();
568 bool strictRules = (recordingChannels <= 2);
587 if (!strictRules && !selectedOnly)
591 std::vector<unsigned> channelCounts;
593 const auto range = trackList.Leaders<
WaveTrack>();
600 unsigned nChannels = channels.size();
602 if (strictRules && nChannels > recordingChannels) {
607 channelCounts.clear();
613 nChannels + candidates.size() > recordingChannels) {
614 auto nOldChannels = channelCounts[0];
615 wxASSERT(nOldChannels > 0);
616 channelCounts.erase(channelCounts.begin());
617 candidates.erase(candidates.begin(),
618 candidates.begin() + nOldChannels);
620 channelCounts.push_back(nChannels);
621 for (
auto channel : channels ) {
622 candidates.push_back(channel->SharedPointer<
WaveTrack>());
623 if(candidates.size() == recordingChannels)
630 if (!strictRules && !candidates.empty())
641 bool bPreferNewTrack;
642 gPrefs->Read(
"/GUI/PreferNewTrackRecord", &bPreferNewTrack,
false);
643 const bool appendRecord = (altAppearance == bPreferNewTrack);
650 double t0 = selectedRegion.
t0();
651 double t1 = selectedRegion.t1();
663 const int numberOfSelected{ selectedTracks.numberOfSelected };
664 const bool allSameRate{ selectedTracks.allSameRate };
668 "for recording must all have the same sampling rate"),
669 XO(
"Mismatched Sampling Rates"),
670 wxICON_ERROR | wxCENTRE);
680 existingTracks = ChooseExistingRecordingTracks(*p,
true, rateOfSelected);
681 if (!existingTracks.empty()) {
686 if (numberOfSelected > 0 && rateOfSelected != options.rate) {
688 "Too few tracks are selected for recording at this sample rate.\n"
689 "(Audacity requires two channels at the same sample rate for\n"
690 "each stereo track)"),
691 XO(
"Too Few Compatible Tracks Selected"),
692 wxICON_ERROR | wxCENTRE);
697 existingTracks = ChooseExistingRecordingTracks(*p,
false, options.rate);
698 if (!existingTracks.empty())
700 auto endTime = std::max_element(
701 existingTracks.begin(),
702 existingTracks.end(),
703 [](
const auto& a,
const auto& b) {
704 return a->GetEndTime() < b->GetEndTime();
706 )->get()->GetEndTime();
710 t0 = std::max(t0, endTime);
717 if (t1 <= selectedRegion.t0() && selectedRegion.t1() > selectedRegion.t0()) {
718 t1 = selectedRegion.t1();
731 transportTracks = GetAllPlaybackTracks(
TrackList::Get( *p ),
false,
true);
732 for (
const auto &wt : existingTracks) {
743 options.rate = rateOfSelected;
745 DoRecord(*p, transportTracks, t0, t1, altAppearance, options);
752 gPrefs->Read(wxT(
"/AudioIO/Duplex"), &duplex,
753#ifdef EXPERIMENTAL_DA
764 double t0,
double t1,
768 auto &projectAudioManager = *
this;
782 if (gAudioIO->IsBusy())
785 projectAudioManager.SetAppending( !altAppearance );
787 bool success =
false;
789 auto transportTracks = tracks;
794 const auto p = &project;
798 auto makeNewClipName = [&](
WaveTrack* track) {
799 for (
auto i = 1;; ++i)
802 auto name =
XC(
"%s #%d",
"clip name template").Format(track->GetName(), i).Translation();
803 if (track->FindClipByName(
name) ==
nullptr)
814 auto endTime = wt->GetEndTime();
821 transportTracks.prerollTracks.push_back(wt);
828 auto &src =
static_cast<const WaveTrack&
>(s);
834 auto pending = std::static_pointer_cast<WaveTrack>(
836 updater, wt.get() ) );
842 pending->CreateClip(t0, makeNewClipName(pending.get()));
844 transportTracks.captureTracks.push_back(pending);
849 if( transportTracks.captureTracks.empty() )
851 bool recordingNameCustom, useTrackNumber, useDateStamp, useTimeStamp;
852 wxString defaultTrackName, defaultRecordingTrackName;
856 auto numTracks = trackList.Leaders<
const WaveTrack >().
size();
860 gPrefs->Read(wxT(
"/GUI/TrackNames/RecordingNameCustom"), &recordingNameCustom,
false);
861 gPrefs->Read(wxT(
"/GUI/TrackNames/TrackNumber"), &useTrackNumber,
false);
862 gPrefs->Read(wxT(
"/GUI/TrackNames/DateStamp"), &useDateStamp,
false);
863 gPrefs->Read(wxT(
"/GUI/TrackNames/TimeStamp"), &useTimeStamp,
false);
865 gPrefs->Read(wxT(
"/GUI/TrackNames/RecodingTrackName"), &defaultRecordingTrackName, defaultTrackName);
867 wxString baseTrackName = recordingNameCustom? defaultRecordingTrackName : defaultTrackName;
870 for (
int c = 0; c < recordingChannels; c++) {
873 first = newTrack.get();
878 t0 = newTrack->LongSamplesToTime(newTrack->TimeToLongSamples(t0));
880 t1 = newTrack->LongSamplesToTime(newTrack->TimeToLongSamples(t1));
883 newTrack->SetOffset(t0);
884 wxString nameSuffix = wxString(wxT(
""));
886 if (useTrackNumber) {
887 nameSuffix += wxString::Format(wxT(
"%d"), 1 + (
int) numTracks + c);
891 if (!nameSuffix.empty()) {
892 nameSuffix += wxT(
"_");
894 nameSuffix += wxDateTime::Now().FormatISODate();
898 if (!nameSuffix.empty()) {
899 nameSuffix += wxT(
"_");
901 nameSuffix += wxDateTime::Now().FormatISOTime();
905 nameSuffix.Replace(wxT(
":"), wxT(
"-"));
907 if (baseTrackName.empty()) {
908 newTrack->SetName(nameSuffix);
910 else if (nameSuffix.empty()) {
911 newTrack->SetName(baseTrackName);
914 newTrack->SetName(baseTrackName + wxT(
"_") + nameSuffix);
917 newTrack->CreateClip(t0, makeNewClipName(newTrack.get()));
921 if ((recordingChannels > 2) &&
926 transportTracks.captureTracks.push_back(newTrack);
936 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
937 gAudioIO->AILAInitialize();
940 int token = gAudioIO->StartStream(transportTracks, t0, t1, t1, options);
942 success = (token != 0);
951 auto msg =
XO(
"Error opening recording device.\nError code: %s")
952 .Format( gAudioIO->LastPaErrorString() );
955 XO(
"Error"), msg, wxT(
"Error_opening_sound_device"),
965 auto &projectAudioManager = *
this;
966 bool canStop = projectAudioManager.CanStopAudioStream();
972 bool paused = !projectAudioManager.Paused();
977#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
979 auto project = &mProject;
984 bool bStopInstead = paused &&
986 !scrubber.IsSpeedPlaying() &&
987 !scrubber.IsKeyboardScrubbing();
995 scrubber.Pause(paused);
999 gAudioIO->SetPaused(paused);
1006 mPaused.fetch_xor(1, std::memory_order::memory_order_relaxed);
1011 mPaused.store(0, std::memory_order::memory_order_relaxed);
1016 return mPaused.load(std::memory_order_relaxed) == 1;
1022 const auto project = &mProject;
1028 auto &project = mProject;
1030 mDisplayedRate = rate;
1046 auto &project = mProject;
1051 if (projectAudioIO.GetAudioIOToken() > 0)
1055 if (IsTimerRecordCancelled()) {
1057 history.RollbackState();
1059 ResetTimerRecordCancelled();
1066 history.PushState(
XO(
"Recorded Audio"),
XO(
"Record"),
1072 auto &intervals = gAudioIO->LostCaptureIntervals();
1073 if (intervals.size()) {
1075 mProject.ProcessEvent(evt);
1083 auto &project = mProject;
1086 wxTheApp->CallAfter( [&]{ projectFileIO.AutoSave(
true); });
1091 const auto project = &mProject;
1097 auto& project = mProject;
1099 if (gAudioIO && &project == gAudioIO->GetOwningProject().get())
1101 bool canStop = CanStopAudioStream();
1103 gAudioIO->SetPaused(!gAudioIO->IsPaused());
1124 gAudioIO->IsBusy() &&
1125 CanStopAudioStream() &&
1127 !gAudioIO->IsMonitoring() &&
1129 gAudioIO->GetNumCaptureChannels() == 0;
1136 gAudioIO->IsBusy() &&
1137 CanStopAudioStream() &&
1138 gAudioIO->GetNumCaptureChannels() > 0;
1144 return (!gAudioIO->IsStreamActive() ||
1145 gAudioIO->IsMonitoring() ||
1146 gAudioIO->GetOwningProject().get() == &mProject );
1153 bool canStop = projectAudioManager.CanStopAudioStream();
1164 options.captureMeter = projectAudioIO.GetCaptureMeter();
1165 options.playbackMeter = projectAudioIO.GetPlaybackMeter();
1170 options.loopEnabled = loopEnabled;
1175 options.policyFactory = [&project, trackEndTime, loopEndTime](
1177 -> std::unique_ptr<PlaybackPolicy>
1179 return std::make_unique<NewDefaultPlaybackPolicy>( project,
1180 trackEndTime, loopEndTime,
1181 options.loopEnabled, options.variableSpeed);
1185 options.pStartTime.emplace(
ViewInfo::Get(project).selectedRegion.t0());
1196 auto PlayAtSpeedRate = gAudioIO->GetBestRate(
1201 result.rate = PlayAtSpeedRate;
1206 TrackList &trackList,
bool selectedOnly,
bool nonWaveToo)
1212 for (
auto pTrack: range)
1216#ifdef EXPERIMENTAL_MIDI_OUT
1220 for (
auto pTrack: range)
1221 if (!track_cast<const WaveTrack *>(pTrack))
1240 auto &project = mProject;
1244 auto &selection = viewInfo.selectedRegion;
1248 if (scrubber.HasMark() ||
1249 gAudioIO->IsStreamActive(token)) {
1251 auto time = gAudioIO->GetStreamTime();
1254 if (click && (scrubber.WasSpeedPlaying() || scrubber.WasKeyboardScrubbing()))
1258 else if (shift && click) {
1260 auto t0 = selection.t0(), t1 = selection.t1();
1269 if (fabs(t0 - time) < fabs(t1 - time))
1274 selection.setTimes(t0, t1);
1278 time = wxMax( time, 0 );
1280 selection.setTimes(time, time);
1284 selection.setT0(time,
false);
1297 if (DoPlayStopSelect(
false,
false))
1299 else if (!gAudioIO->IsBusy()) {
1303 PlayCurrentRegion(
false);
1330 const auto selectedTracks{
1333 for (
const auto & track : selectedTracks)
1336 track->GetRate() != rateOfSelection)
1339 rateOfSelection = track->GetRate();
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
std::vector< std::shared_ptr< WaveTrack > > WaveTrackArray
IntSetting AudioIORecordChannels
Toolkit-neutral facade for basic user interface services.
constexpr CommandFlag AlwaysEnabledFlag
std::bitset< NCommandFlags > CommandFlag
const ReservedCommandFlag & AudioIONotBusyFlag()
const ReservedCommandFlag & PausedFlag()
const TranslatableString name
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
constexpr size_t TimeQueueGrainSize
static TranslatableString FormatRate(int rate)
wxDEFINE_EVENT(EVT_RECORDING_DROPOUT, RecordingDropoutEvent)
AudioIOStartStreamOptions DefaultPlayOptions(AudacityProject &project, bool newDefault)
AudioIOStartStreamOptions DefaultSpeedPlayOptions(AudacityProject &project)
static RegisteredMenuItemEnabler stopIfPaused
static AudacityProject::AttachedObjects::RegisteredFactory sProjectAudioManagerKey
const ReservedCommandFlag & CanStopAudioStreamFlag()
PropertiesOfSelected GetPropertiesOfSelected(const AudacityProject &proj)
constexpr int RATE_NOT_SELECTED
an object holding per-project preferred sample rate
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
accessors for certain important windows associated with each project
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
static AudioIOBase * Get()
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
AudioTrack subclass that can also be audibly replayed by the program.
Directs which parts of tracks to fetch for playback.
std::vector< std::unique_ptr< Mixer > > Mixers
virtual void Initialize(PlaybackSchedule &schedule, double rate)
Called before starting an audio stream.
int GetAudioIOToken() const
void SetAudioIOToken(int token)
static ProjectAudioIO & Get(AudacityProject &project)
void Stop(bool stopStream=true)
static std::pair< TranslatableStrings, unsigned > StatusWidthFunction(const AudacityProject &project, StatusBarField field)
static WaveTrackArray ChooseExistingRecordingTracks(AudacityProject &proj, bool selectedOnly, double targetRate=RATE_NOT_SELECTED)
bool CanStopAudioStream() const
void OnAudioIOStartRecording() override
void OnCommitRecording() override
static TransportTracks GetAllPlaybackTracks(TrackList &trackList, bool selectedOnly, bool nonWaveToo=false)
void OnAudioIORate(int rate) override
static ProjectAudioManager & Get(AudacityProject &project)
void OnAudioIOStopRecording() override
void OnAudioIONewBlocks(const WaveTrackArray *tracks) override
~ProjectAudioManager() override
ProjectAudioManager(AudacityProject &project)
int PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode playMode, bool backwards=false)
void OnCheckpointFailure(wxCommandEvent &evt)
void PlayCurrentRegion(bool newDefault=false, bool cutpreview=false)
void OnRecord(bool altAppearance)
bool DoRecord(AudacityProject &project, const TransportTracks &transportTracks, double t0, double t1, bool altAppearance, const AudioIOStartStreamOptions &options)
void OnSoundActivationThreshold() override
static ProjectFileIO & Get(AudacityProject &project)
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static ProjectRate & Get(AudacityProject &project)
static ProjectSettings & Get(AudacityProject &project)
bool GetTracksFitVerticallyZoomed() const
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)
Defines a selected portion of a project.
bool Read(T *pVar) const
overload of Read returning a boolean that is true if the value was previously defined */
Abstract base class for an object holding data associated with points on a time axis.
virtual double GetEndTime() const =0
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
void RegisterPendingNewTrack(const std::shared_ptr< Track > &pTrack)
bool MakeMultiChannelTrack(Track &first, int nChannels, bool aligned)
Converts channels to a multichannel track.
double GetEndTime() const
auto Any() -> TrackIterRange< TrackType >
bool ApplyPendingTracks()
static TrackList & Get(AudacityProject &project)
std::shared_ptr< Track > RegisterPendingChangedTrack(Updater updater, Track *src)
void ClearPendingTracks(ListOfTracks *pAdded=nullptr)
void UpdatePendingTracks()
auto Selected() -> TrackIterRange< TrackType >
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
void SetMinimized(bool minimized)
static TrackView & Get(Track &)
Holds a msgid for the translation catalog; may also bind format arguments.
NotifyingSelectedRegion selectedRegion
static ViewInfo & Get(AudacityProject &project)
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
static WaveTrackFactory & Get(AudacityProject &project)
A Track that contains audio waveform data.
void Reinit(const WaveTrack &orig)
static wxString GetDefaultAudioTrackNamePreference()
std::pair< double, double > AdvancedTrackTime(PlaybackSchedule &schedule, double trackTime, size_t nSamples) override
Compute a new point in a track's timeline from an old point and a real duration.
PlaybackSlice GetPlaybackSlice(PlaybackSchedule &schedule, size_t available) override
Choose length of one fetch of samples from tracks in a call to AudioIO::FillPlayBuffers.
double OffsetTrackTime(PlaybackSchedule &schedule, double offset) override
Called when the play head needs to jump a certain distance.
void Initialize(PlaybackSchedule &schedule, double rate) override
Called before starting an audio stream.
double mStart
Starting and ending track times set in Initialize()
bool Done(PlaybackSchedule &schedule, unsigned long) override
Returns true if schedule.GetTrackTime() has reached the end of playback.
~CutPreviewPlaybackPolicy() override
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.
bool AtOrBefore(double trackTime1, double trackTime2) const
Positions or offsets within audio files need a wide type.
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.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
void swap(std::unique_ptr< Alg_seq > &a, std::unique_ptr< Alg_seq > &b)
struct holding stream options, including a pointer to the time warp info and AudioIOListener and whet...
PolicyFactory policyFactory
std::optional< double > pStartTime
Options for variations of error dialogs; the default is for modal dialogs.
double GetTrackTime() const
Get current track time value, unadjusted.
double mT0
Playback starts at offset of mT0, which is measured in seconds.
double mT1
Playback ends at offset of mT1, which is measured in seconds. Note that mT1 may be less than mT0 duri...
double SolveWarpedLength(double t0, double length) const
Compute how much unwarped time must have elapsed if length seconds of warped time has elapsed,...
double ComputeWarpedLength(double t0, double t1) const
Compute signed duration (in seconds at playback) of the specified region of the track.
Describes an amount of contiguous (but maybe time-warped) data to be extracted from tracks to play.
Notification, posted on the project, after recording has stopped, when dropouts have been detected.
static bool IsScrubbing()
PlayableTrackConstArray otherPlayableTracks
WaveTrackArray captureTracks
WaveTrackArray playbackTracks