16#include <wx/statusbr.h>
49 return std::make_shared< ProjectAudioManager >( project );
79 return XO(
"Actual Rate: %d").Format( rate );
92 int rate = audioManager.mDisplayedRate;
121 double trackTime,
size_t nSamples)
override;
123 bool RepositionPlayback(
125 size_t frames,
size_t available)
override;
129 {
return mReversed ? mGapLeft + mGapLength : mGapLeft; }
131 {
return mReversed ? mGapLeft : mGapLeft + mGapLength; }
133 {
return mReversed ? trackTime1 >= trackTime2 : trackTime1 <= trackTime2; }
139 double mStart = 0, mEnd = 0;
142 double mDuration1 = 0, mDuration2 = 0;
143 double mInitDuration1 = 0, mInitDuration2 = 0;
145 bool mDiscontinuity{
false };
146 bool mReversed{
false };
149CutPreviewPlaybackPolicy::CutPreviewPlaybackPolicy(
150 double gapLeft,
double gapLength)
151: mGapLeft{ gapLeft }, mGapLength{ gapLength }
153 wxASSERT(gapLength >= 0.0);
165 double right =
mEnd = schedule.
mT1;
198 auto space = std::clamp(
mGapLeft - time, 0.0, offset);
232 size_t frames = available;
233 size_t toProduce = frames;
235 if (samples1 > 0 && samples1 < frames)
237 toProduce = frames = samples1.
as_size_t();
238 else if (samples1 == 0) {
240 if (samples2 < frames) {
247 return { available, frames, toProduce };
253 auto realDuration = nSamples /
mRate;
269 return {
mEnd, std::numeric_limits<double>::infinity()};
275 const Mixers &playbackMixers,
size_t,
size_t )
280 for (
auto &pMixer : playbackMixers)
281 pMixer->Reposition(newTime,
true);
294 auto &projectAudioManager = *
this;
295 bool canStop = projectAudioManager.CanStopAudioStream();
307 double t0 = selectedRegion.
t0();
308 double t1 = selectedRegion.
t1();
319 bool success =
false;
322 if (gAudioIO->IsBusy())
326 if (cutpreview && t0==t1)
333 mLastPlayMode = mode;
339 hasaudio = ! tracks.Any<
WaveTrack>().empty();
341 double latestEnd = tracks.GetEndTime();
346#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
347 double initSeek = 0.0;
349 double loopOffset = 0.0;
358 if ((t0 > selectedRegion.
t0()) && (t0 < selectedRegion.
t1())) {
359 t0 = selectedRegion.
t0();
360 t1 = selectedRegion.
t1();
365 loopOffset = t0 - tracks.GetStartTime();
369 pStartTime.emplace(loopOffset);
370 t0 = tracks.GetStartTime();
371 t1 = tracks.GetEndTime();
376 t0 = tracks.GetStartTime();
378 else if (t0 > tracks.GetEndTime()) {
379 t0 = tracks.GetEndTime();
381#if defined(EXPERIMENTAL_SEEK_BEHIND_CURSOR)
385 pStartTime.emplace(initSeek);
386 t0 = tracks.GetStartTime();
390 t1 = tracks.GetEndTime();
397 t0 = std::max(0.0,
std::min(t0, latestEnd));
398 t1 = std::max(0.0,
std::min(t1, latestEnd));
408 const double tless =
std::min(t0, t1);
409 const double tgreater = std::max(t0, t1);
410 double beforeLen, afterLen;
411 gPrefs->Read(
wxT(
"/AudioIO/CutPreviewBeforeLen"), &beforeLen, 2.0);
412 gPrefs->Read(
wxT(
"/AudioIO/CutPreviewAfterLen"), &afterLen, 1.0);
413 double tcp0 = tless-beforeLen;
414 const double diff = tgreater - tless;
415 double tcp1 = tgreater+afterLen;
420 [tless, diff](
auto&) -> std::unique_ptr<PlaybackPolicy> {
421 return std::make_unique<CutPreviewPlaybackPolicy>(tless, diff);
423 token = gAudioIO->StartStream(
425 tcp0, tcp1, tcp1, myOptions);
428 double mixerLimit = t1;
430 mixerLimit = latestEnd;
431 if (pStartTime && *pStartTime >= t1)
434 token = gAudioIO->StartStream(
436 t0, t1, mixerLimit, options);
449 window.CallAfter( [&]{
454 XO(
"Error opening sound device.\nTry changing the audio host, playback device and the project sample rate."),
455 wxT(
"Error_opening_sound_device"),
470 auto &projectAudioManager = *
this;
471 bool canStop = projectAudioManager.CanStopAudioStream();
486 options.envelope =
nullptr;
491 PlayPlayRegion(
SelectedRegion(playRegion.GetStart(), playRegion.GetEnd()),
500 auto &projectAudioManager = *
this;
501 bool canStop = projectAudioManager.CanStopAudioStream();
509 scrubber.StopScrubbing();
514 auto cleanup =
finally( [&]{
515 projectAudioManager.SetStopping(
false );
518 if (stopStream && gAudioIO->IsBusy()) {
520 projectAudioManager.SetStopping(
true );
522 while( wxTheApp->ProcessIdle() )
527 gAudioIO->StopStream();
529 projectAudioManager.SetLooping(
false );
530 projectAudioManager.SetCutting(
false );
532 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
533 gAudioIO->AILADisable();
536 projectAudioManager.SetPausedOff();
538 gAudioIO->SetPaused(
false );
544 auto meter = projectAudioIO.GetPlaybackMeter();
549 meter = projectAudioIO.GetCaptureMeter();
567 bool strictRules = (recordingChannels <= 2);
586 if (!strictRules && !selectedOnly)
590 std::vector<unsigned> channelCounts;
592 const auto range = trackList.Leaders<
WaveTrack>();
599 unsigned nChannels = channels.size();
601 if (strictRules && nChannels > recordingChannels) {
606 channelCounts.clear();
612 nChannels + candidates.size() > recordingChannels) {
613 auto nOldChannels = channelCounts[0];
614 wxASSERT(nOldChannels > 0);
615 channelCounts.erase(channelCounts.begin());
616 candidates.erase(candidates.begin(),
617 candidates.begin() + nOldChannels);
619 channelCounts.push_back(nChannels);
620 for (
auto channel : channels ) {
621 candidates.push_back(channel->SharedPointer<
WaveTrack>());
622 if(candidates.size() == recordingChannels)
629 if (!strictRules && !candidates.empty())
640 bool bPreferNewTrack;
641 gPrefs->Read(
"/GUI/PreferNewTrackRecord", &bPreferNewTrack,
false);
642 const bool appendRecord = (altAppearance == bPreferNewTrack);
649 double t0 = selectedRegion.
t0();
650 double t1 = selectedRegion.t1();
662 const int numberOfSelected{ selectedTracks.numberOfSelected };
663 const bool allSameRate{ selectedTracks.allSameRate };
667 "for recording must all have the same sampling rate"),
668 XO(
"Mismatched Sampling Rates"),
669 wxICON_ERROR | wxCENTRE);
679 existingTracks = ChooseExistingRecordingTracks(*p,
true, rateOfSelected);
680 if (!existingTracks.empty()) {
685 if (numberOfSelected > 0 && rateOfSelected != options.rate) {
687 "Too few tracks are selected for recording at this sample rate.\n"
688 "(Audacity requires two channels at the same sample rate for\n"
689 "each stereo track)"),
690 XO(
"Too Few Compatible Tracks Selected"),
691 wxICON_ERROR | wxCENTRE);
696 existingTracks = ChooseExistingRecordingTracks(*p,
false, options.rate);
697 if (!existingTracks.empty())
699 auto endTime = std::max_element(
700 existingTracks.begin(),
701 existingTracks.end(),
702 [](
const auto& a,
const auto& b) {
703 return a->GetEndTime() < b->GetEndTime();
705 )->get()->GetEndTime();
709 t0 = std::max(t0, endTime);
716 if (t1 <= selectedRegion.t0() && selectedRegion.t1() > selectedRegion.t0()) {
717 t1 = selectedRegion.t1();
731 for (
const auto &wt : existingTracks) {
742 options.rate = rateOfSelected;
744 DoRecord(*p, transportTracks, t0, t1, altAppearance, options);
751 gPrefs->Read(
wxT(
"/AudioIO/Duplex"), &duplex,
752#ifdef EXPERIMENTAL_DA
763 double t0,
double t1,
767 auto &projectAudioManager = *
this;
781 if (gAudioIO->IsBusy())
784 projectAudioManager.SetAppending( !altAppearance );
786 bool success =
false;
788 auto transportTracks = tracks;
793 const auto p = &project;
797 auto makeNewClipName = [&](
WaveTrack* track) {
798 for (
auto i = 1;; ++i)
801 auto name =
XC(
"%s #%d",
"clip name template").Format(track->GetName(), i).Translation();
802 if (track->FindClipByName(
name) ==
nullptr)
813 auto endTime = wt->GetEndTime();
820 transportTracks.prerollTracks.push_back(wt);
827 auto &src =
static_cast<const WaveTrack&
>(s);
833 auto pending = std::static_pointer_cast<WaveTrack>(
841 pending->CreateClip(t0, makeNewClipName(pending.get()));
843 transportTracks.captureTracks.push_back(pending);
848 if( transportTracks.captureTracks.empty() )
850 bool recordingNameCustom, useTrackNumber, useDateStamp, useTimeStamp;
851 wxString defaultTrackName, defaultRecordingTrackName;
855 auto numTracks = trackList.Leaders<
const WaveTrack >().
size();
859 gPrefs->Read(
wxT(
"/GUI/TrackNames/RecordingNameCustom"), &recordingNameCustom,
false);
860 gPrefs->Read(
wxT(
"/GUI/TrackNames/TrackNumber"), &useTrackNumber,
false);
861 gPrefs->Read(
wxT(
"/GUI/TrackNames/DateStamp"), &useDateStamp,
false);
862 gPrefs->Read(
wxT(
"/GUI/TrackNames/TimeStamp"), &useTimeStamp,
false);
864 gPrefs->Read(
wxT(
"/GUI/TrackNames/RecodingTrackName"), &defaultRecordingTrackName, defaultTrackName);
866 wxString baseTrackName = recordingNameCustom? defaultRecordingTrackName : defaultTrackName;
869 for (
int c = 0; c < recordingChannels; c++) {
872 first = newTrack.get();
877 t0 = newTrack->LongSamplesToTime(newTrack->TimeToLongSamples(t0));
879 t1 = newTrack->LongSamplesToTime(newTrack->TimeToLongSamples(t1));
882 newTrack->SetOffset(t0);
883 wxString nameSuffix = wxString(
wxT(
""));
885 if (useTrackNumber) {
886 nameSuffix += wxString::Format(
wxT(
"%d"), 1 + (
int) numTracks + c);
890 if (!nameSuffix.empty()) {
891 nameSuffix +=
wxT(
"_");
893 nameSuffix += wxDateTime::Now().FormatISODate();
897 if (!nameSuffix.empty()) {
898 nameSuffix +=
wxT(
"_");
900 nameSuffix += wxDateTime::Now().FormatISOTime();
904 nameSuffix.Replace(
wxT(
":"),
wxT(
"-"));
906 if (baseTrackName.empty()) {
907 newTrack->SetName(nameSuffix);
909 else if (nameSuffix.empty()) {
910 newTrack->SetName(baseTrackName);
913 newTrack->SetName(baseTrackName +
wxT(
"_") + nameSuffix);
916 newTrack->CreateClip(t0, makeNewClipName(newTrack.get()));
920 if ((recordingChannels > 2) &&
925 transportTracks.captureTracks.push_back(newTrack);
935 #ifdef EXPERIMENTAL_AUTOMATED_INPUT_LEVEL_ADJUSTMENT
936 gAudioIO->AILAInitialize();
939 int token = gAudioIO->StartStream(transportTracks, t0, t1, t1, options);
941 success = (token != 0);
950 auto msg =
XO(
"Error opening recording device.\nError code: %s")
951 .Format( gAudioIO->LastPaErrorString() );
954 XO(
"Error"), msg,
wxT(
"Error_opening_sound_device"),
964 auto &projectAudioManager = *
this;
965 bool canStop = projectAudioManager.CanStopAudioStream();
971 bool paused = !projectAudioManager.Paused();
976#ifdef EXPERIMENTAL_SCRUBBING_SUPPORT
978 auto project = &mProject;
983 bool bStopInstead = paused &&
985 !scrubber.IsSpeedPlaying() &&
986 !scrubber.IsKeyboardScrubbing();
994 scrubber.Pause(paused);
998 gAudioIO->SetPaused(paused);
1005 mPaused.fetch_xor(1, std::memory_order::memory_order_relaxed);
1010 mPaused.store(0, std::memory_order::memory_order_relaxed);
1015 return mPaused.load(std::memory_order_relaxed) == 1;
1021 const auto project = &mProject;
1027 auto &project = mProject;
1029 mDisplayedRate = rate;
1045 auto &project = mProject;
1050 if (projectAudioIO.GetAudioIOToken() > 0)
1054 if (IsTimerRecordCancelled()) {
1056 history.RollbackState();
1058 ResetTimerRecordCancelled();
1068 history.PushState(
XO(
"Recorded Audio"),
XO(
"Record"), flags);
1073 auto &intervals = gAudioIO->LostCaptureIntervals();
1074 if (intervals.size())
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();
1168 options.loopEnabled = loopEnabled;
1173 options.policyFactory = [&project, trackEndTime, loopEndTime](
1175 -> std::unique_ptr<PlaybackPolicy>
1177 return std::make_unique<DefaultPlaybackPolicy>( project,
1178 trackEndTime, loopEndTime,
1179 options.loopEnabled, options.variableSpeed);
1183 options.pStartTime.emplace(
ViewInfo::Get(project).selectedRegion.t0());
1194 auto PlayAtSpeedRate = gAudioIO->GetBestRate(
1199 result.rate = PlayAtSpeedRate;
1212 auto &project = mProject;
1216 auto &selection = viewInfo.selectedRegion;
1220 if (scrubber.HasMark() ||
1221 gAudioIO->IsStreamActive(token)) {
1223 auto time = gAudioIO->GetStreamTime();
1226 if (click && (scrubber.WasSpeedPlaying() || scrubber.WasKeyboardScrubbing()))
1230 else if (shift && click) {
1232 auto t0 = selection.t0(), t1 = selection.t1();
1241 if (fabs(t0 - time) < fabs(t1 - time))
1246 selection.setTimes(t0, t1);
1250 time = wxMax( time, 0 );
1252 selection.setTimes(time, time);
1256 selection.setT0(time,
false);
1269 if (DoPlayStopSelect(
false,
false))
1271 else if (!gAudioIO->IsBusy()) {
1275 PlayCurrentRegion(
false);
1300 const auto selectedTracks{
1303 for (
const auto & track : selectedTracks)
1306 track->GetRate() != rateOfSelection)
1309 rateOfSelection = track->GetRate();
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
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
std::unique_ptr< const BasicUI::WindowPlacement > ProjectFramePlacement(AudacityProject *project)
Make a WindowPlacement object suitable for project (which may be null)
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)
std::vector< std::shared_ptr< WritableSampleTrack > > WritableSampleTrackArray
constexpr int RATE_NOT_SELECTED
ProjectFileIOMessage
Subscribe to ProjectFileIO to receive messages; always in idle time.
@ CheckpointFailure
Failure happened in a worker thread.
an object holding per-project preferred sample 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
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
static AudioIOBase * Get()
bool HasRecordingException() const
Client code makes static instance from a factory of attachments; passes it to Get or Find as a retrie...
typename GlobalVariable< DefaultOptions, const std::function< std::remove_pointer_t< decltype(DefaultFunction)> >, Default, Options... >::Scope Scope
Subscription Subscribe(Callback callback)
Connect a callback to the Publisher; later-connected are called earlier.
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.
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)
Observer::Subscription mCheckpointFailureSubcription
void Stop(bool stopStream=true)
static std::pair< TranslatableStrings, unsigned > StatusWidthFunction(const AudacityProject &project, StatusBarField field)
bool CanStopAudioStream() const
void OnAudioIOStartRecording() override
void OnCommitRecording() override
static WritableSampleTrackArray ChooseExistingRecordingTracks(AudacityProject &proj, bool selectedOnly, double targetRate=RATE_NOT_SELECTED)
void OnAudioIORate(int rate) override
static ProjectAudioManager & Get(AudacityProject &project)
void OnCheckpointFailure(ProjectFileIOMessage)
void OnAudioIOStopRecording() override
void OnAudioIONewBlocks(const WritableSampleTrackArray *tracks) override
~ProjectAudioManager() override
ProjectAudioManager(AudacityProject &project)
int PlayPlayRegion(const SelectedRegion &selectedRegion, const AudioIOStartStreamOptions &options, PlayMode playMode, bool backwards=false)
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
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)
double GetRate(const Track &track)
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, after recording has stopped, when dropouts have been detected.
static bool IsScrubbing()
WritableSampleTrackArray captureTracks
SampleTrackConstArray playbackTracks