Audacity 3.2.0
Dependencies.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4 Audacity(R) is copyright (c) 1999-2008 Audacity Team.
5 License: GPL v2 or later. See License.txt.
6
7 Dependencies.cpp
8
9 Dominic Mazzoni
10 Leland Lucius
11 Markus Meyer
12 LRN
13 Michael Chinen
14 Vaughan Johnson
15
16 The primary function provided in this source file is
17 ShowDependencyDialogIfNeeded. It checks a project to see if
18 any of its WaveTracks contain AliasBlockFiles; if so it
19 presents a dialog to the user and lets them copy those block
20 files into the project, making it self-contained.
21
22********************************************************************//*****************************************************************//********************************************************************/
36
37#include "Audacity.h"
38#include "Dependencies.h"
39
40#include <wx/defs.h>
41#include <wx/filename.h>
42#include <wx/listctrl.h>
43#include <wx/menu.h>
44#include <wx/choice.h>
45#include <wx/clipbrd.h>
46#include <wx/dataobj.h>
47#include <wx/frame.h>
48#include <wx/stattext.h>
49
50#include "blockfile/SimpleBlockFile.h"
51#include "DirManager.h"
52#include "FileFormats.h"
53#include "Prefs.h"
54#include "Project.h"
55#include "Sequence.h"
56#include "ShuttleGui.h"
57#include "WaveTrack.h"
58#include "WaveClip.h"
59#include "prefs/QualityPrefs.h"
60#include "widgets/AudacityMessageBox.h"
61#include "widgets/ProgressDialog.h"
62
63#include <unordered_map>
64
65using AliasedFileHash = std::unordered_map<wxString, AliasedFile*>;
66
67// These two hash types are used only inside short scopes
68// so it is safe to key them by plain pointers.
69using ReplacedBlockFileHash = std::unordered_map<BlockFile *, BlockFilePtr>;
70using BoolBlockFileHash = std::unordered_map<BlockFile *, bool>;
71
72// Given a project, returns a single array of all SeqBlocks
73// in the current set of tracks. Enumerating that array allows
74// you to process all block files in the current set.
76 BlockPtrArray *outBlocks)
77{
78 for (auto waveTrack : TrackList::Get( *project ).Any< WaveTrack >()) {
79 for(const auto &clip : waveTrack->GetAllClips()) {
80 Sequence *sequence = clip->GetSequence();
81 BlockArray &blocks = sequence->GetBlockArray();
82 for (size_t i = 0; i < blocks.size(); i++)
83 outBlocks->push_back(&blocks[i]);
84 }
85 }
86}
87
88// Given an Audacity project and a hash mapping aliased block
89// files to un-aliased block files, walk through all of the
90// tracks and replace each aliased block file with its replacement.
91// Note that this code respects reference-counting and thus the
92// process of making a project self-contained is actually undoable.
93static void ReplaceBlockFiles(BlockPtrArray &blocks,
95// NOFAIL-GUARANTEE
96{
97 for (const auto &pBlock : blocks) {
98 auto &f = pBlock->f;
99 const auto src = &*f;
100 if (hash.count( src ) > 0) {
101 const auto &dst = hash[src];
102 f = dst;
103 }
104 }
105}
106
108 AliasedFileArray &outAliasedFiles)
109{
111
112 BlockPtrArray blocks;
113 GetAllSeqBlocks(project, &blocks);
114
115 AliasedFileHash aliasedFileHash;
116 BoolBlockFileHash blockFileHash;
117
118 for (const auto &blockFile : blocks) {
119 const auto &f = blockFile->f;
120 if (f->IsAlias() && (blockFileHash.count( &*f ) == 0))
121 {
122 // f is an alias block we have not yet counted.
123 blockFileHash[ &*f ] = true; // Don't count the same blockfile twice.
124 auto aliasBlockFile = static_cast<AliasBlockFile*>( &*f );
125 const wxFileName &fileName = aliasBlockFile->GetAliasedFileName();
126
127 // In ProjectFSCK(), if the user has chosen to
128 // "Replace missing audio with silence", the code there puts in an empty wxFileName.
129 // Don't count those in dependencies.
130 if (!fileName.IsOk())
131 continue;
132
133 const wxString &fileNameStr = fileName.GetFullPath();
134 auto blockBytes = (SAMPLE_SIZE(format) *
135 aliasBlockFile->GetLength());
136 if (aliasedFileHash.count(fileNameStr) > 0)
137 // Already put this AliasBlockFile in aliasedFileHash.
138 // Update block count.
139 aliasedFileHash[fileNameStr]->mByteCount += blockBytes;
140 else
141 {
142 // Haven't counted this AliasBlockFile yet.
143 // Add to return array and internal hash.
144
145 // PRL: do this in two steps so that we move instead of copying.
146 // We don't have a moving push_back in all compilers.
147 outAliasedFiles.push_back(AliasedFile{});
148 outAliasedFiles.back() =
150 wxFileNameWrapper { fileName },
151 wxLongLong(blockBytes), fileName.FileExists()
152 };
153 aliasedFileHash[fileNameStr] = &outAliasedFiles.back();
154 }
155 }
156 }
157}
158
159// Given a project and a list of aliased files that should no
160// longer be external dependencies (selected by the user), replace
161// all of those alias block files with disk block files.
163 AliasedFileArray &aliasedFiles)
164// STRONG-GUARANTEE
165{
166 auto &dirManager = DirManager::Get( *project );
167
168 ProgressDialog progress(
169 XO("Removing Dependencies"),
170 XO("Copying audio data into project..."));
171 auto updateResult = ProgressResult::Success;
172
173 // Hash aliasedFiles based on their full paths and
174 // count total number of bytes to process.
175 AliasedFileHash aliasedFileHash;
176 wxLongLong totalBytesToProcess = 0;
177 for (auto &aliasedFile : aliasedFiles) {
178 totalBytesToProcess += aliasedFile.mByteCount;
179 const wxString &fileNameStr = aliasedFile.mFileName.GetFullPath();
180 aliasedFileHash[fileNameStr] = &aliasedFile;
181 }
182
183 BlockPtrArray blocks;
184 GetAllSeqBlocks(project, &blocks);
185
187 ReplacedBlockFileHash blockFileHash;
188 wxLongLong completedBytes = 0;
189 for (const auto blockFile : blocks) {
190 const auto &f = blockFile->f;
191 if (f->IsAlias() && (blockFileHash.count( &*f ) == 0))
192 {
193 // f is an alias block we have not yet processed.
194 auto aliasBlockFile = static_cast<AliasBlockFile*>( &*f );
195 const wxFileName &fileName = aliasBlockFile->GetAliasedFileName();
196 const wxString &fileNameStr = fileName.GetFullPath();
197
198 if (aliasedFileHash.count(fileNameStr) == 0)
199 // This aliased file was not selected to be replaced. Skip it.
200 continue;
201
202 // Convert it from an aliased file to an actual file in the project.
203 auto len = aliasBlockFile->GetLength();
204 BlockFilePtr newBlockFile;
205 {
206 SampleBuffer buffer(len, format);
207 // We tolerate exceptions from NewBlockFile
208 // and so we can allow exceptions from ReadData too
209 f->ReadData(buffer.ptr(), format, 0, len);
210 newBlockFile =
211 dirManager.NewBlockFile( [&]( wxFileNameWrapper filePath ) {
212 return make_blockfile<SimpleBlockFile>(
213 std::move(filePath), buffer.ptr(), len, format);
214 } );
215 }
216
217 // Update our hash so we know what block files we've done
218 blockFileHash[ &*f ] = newBlockFile;
219
220 // Update the progress bar
221 completedBytes += SAMPLE_SIZE(format) * len;
222 updateResult = progress.Update(completedBytes, totalBytesToProcess);
223 if (updateResult != ProgressResult::Success)
224 // leave the project unchanged
225 return;
226 }
227 }
228
229 // COMMIT OPERATIONS needing NOFAIL-GUARANTEE:
230
231 // Above, we created a SimpleBlockFile contained in our project
232 // to go with each AliasBlockFile that we wanted to migrate.
233 // However, that didn't actually change any references to these
234 // blockfiles in the Sequences, so we do that next...
235 ReplaceBlockFiles(blocks, blockFileHash);
236}
237
238//
239// DependencyDialog
240//
241
243{
244public:
245 DependencyDialog(wxWindow *parent,
246 wxWindowID id,
248 AliasedFileArray &aliasedFiles,
249 bool isSaving);
250
251private:
252 void PopulateList();
254
255 // event handlers
256 void OnCancel(wxCommandEvent& evt);
257 void OnCopySelectedFiles(wxCommandEvent &evt);
258 void OnList(wxListEvent &evt);
259 void OnSize(wxSizeEvent &evt);
260 void OnNo(wxCommandEvent &evt);
261 void OnYes(wxCommandEvent &evt);
262 void OnRightClick(wxListEvent& evt);
263 void OnCopyToClipboard( wxCommandEvent& evt );
264
265
267
268
274
275 wxStaticText *mMessageStaticText;
276 wxListCtrl *mFileListCtrl;
280
281public:
282 DECLARE_EVENT_TABLE()
283};
284
285enum {
291
292BEGIN_EVENT_TABLE(DependencyDialog, wxDialogWrapper)
294 EVT_LIST_ITEM_DESELECTED(FileListID, DependencyDialog::OnList)
295 EVT_LIST_ITEM_RIGHT_CLICK(FileListID, DependencyDialog::OnRightClick )
298 EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) // mIsSaving ? "Cancel Save" : "Save Without Copying"
299 EVT_BUTTON(wxID_YES, DependencyDialog::OnYes) // "Copy All Files (Safer)"
300 EVT_BUTTON(wxID_CANCEL, DependencyDialog::OnCancel) // "Cancel Save"
303
304DependencyDialog::DependencyDialog(wxWindow *parent,
305 wxWindowID id,
307 AliasedFileArray &aliasedFiles,
308 bool isSaving)
309: wxDialogWrapper(parent, id, XO("Project Depends on Other Audio Files"),
310 wxDefaultPosition, wxDefaultSize,
311 (isSaving ?
312 (wxDEFAULT_DIALOG_STYLE & ~wxCLOSE_BOX) : // no close box when saving
313 wxDEFAULT_DIALOG_STYLE) |
314 wxRESIZE_BORDER),
315 mProject(project),
316 mAliasedFiles(aliasedFiles),
317 mIsSaving(isSaving),
318 mHasMissingFiles(false),
319 mHasNonMissingFiles(false),
320 mMessageStaticText(NULL),
321 mFileListCtrl(NULL),
322 mCopySelectedFilesButton(NULL),
323 mCopyAllFilesButton(NULL),
324 mFutureActionChoice(NULL)
325{
326 SetName();
327 ShuttleGui S(this, eIsCreating);
328 PopulateOrExchange(S);
329}
330
332{
333 return
334XO("Copying these files into your project will remove this dependency.\
335\nThis is safer, but needs more disk space.");
336}
337
339{
340 return
341XO("\n\nFiles shown as MISSING have been moved or deleted and cannot be copied.\
342\nRestore them to their original location to be able to copy into project.");
343}
344
346{
347 S.SetBorder(5);
348 S.StartVerticalLay();
349 {
350 mMessageStaticText = S.AddVariableText(kStdMsg(), false);
351
352 S.StartStatic(XO("Project Dependencies"),1);
353 {
354 mFileListCtrl = S.Id(FileListID).AddListControlReportMode({
355 { XO("Audio File"), wxLIST_FORMAT_LEFT, 220 },
356 { XO("Disk Space"), wxLIST_FORMAT_LEFT, 120 }
357 });
358 PopulateList();
359
362 .Focus()
363 .Disable(mFileListCtrl->GetSelectedItemCount() <= 0)
364 .AddButton(
365 XXO("Copy Selected Files"),
366 wxALIGN_LEFT, true);
367 }
368 S.EndStatic();
369
370 S.StartHorizontalLay(wxALIGN_CENTRE,0);
371 {
372 if (mIsSaving) {
373 S.Id(wxID_CANCEL).AddButton(XXO("Cancel Save"));
374 S.Id(wxID_NO).AddButton(XXO("Save Without Copying"));
375 }
376 else
377 S.Id(wxID_NO).AddButton(XXO("Do Not Copy"));
378
380 S.Id(wxID_YES)
381 // Enabling mCopyAllFilesButton is also done in PopulateList,
382 // but at its call above, mCopyAllFilesButton does not yet exist.
383 .Disable(mHasMissingFiles)
384 .AddButton(XXO("Copy All Files (Safer)"));
385
386 }
387 S.EndHorizontalLay();
388
389 if (mIsSaving)
390 {
391 S.StartHorizontalLay(wxALIGN_LEFT,0);
392 {
394 S.Id(FutureActionChoiceID).AddChoice(
395 XXO("Whenever a project depends on other files:"),
396 {
397 /*i18n-hint: One of the choices of what you want Audacity to do when
398 * Audacity finds a project depends on another file.*/
399 XO("Ask me") ,
400 /*i18n-hint: One of the choices of what you want Audacity to do when
401 * Audacity finds a project depends on another file.*/
402 XO("Always copy all files (safest)") ,
403 /*i18n-hint: One of the choices of what you want Audacity to do when
404 * Audacity finds a project depends on another file.*/
405 XO("Never copy any files") ,
406 },
407 0 // "Ask me"
408 );
409 }
410 S.EndHorizontalLay();
411 }
412 else
413 {
414 mFutureActionChoice = NULL;
415 }
416 }
417 S.EndVerticalLay();
418 Layout();
419 Fit();
420 SetMinSize(GetSize());
421 Center();
422}
423
425{
426 mFileListCtrl->DeleteAllItems();
427
428 mHasMissingFiles = false;
429 mHasNonMissingFiles = false;
430 long i = 0;
431 for (const auto &aliasedFile : mAliasedFiles) {
432 const wxFileName &fileName = aliasedFile.mFileName;
433 wxLongLong byteCount = (aliasedFile.mByteCount * 124) / 100;
434 bool bOriginalExists = aliasedFile.mbOriginalExists;
435
436 if (bOriginalExists)
437 {
438 mFileListCtrl->InsertItem(i, fileName.GetFullPath());
439 mHasNonMissingFiles = true;
440 mFileListCtrl->SetItemState(i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
441 }
442 else
443 {
444 mFileListCtrl->InsertItem(i,
445 wxString::Format( _("MISSING %s"), fileName.GetFullPath() ) );
446 mHasMissingFiles = true;
447 mFileListCtrl->SetItemState(i, 0, wxLIST_STATE_SELECTED); // Deselect.
448 mFileListCtrl->SetItemTextColour(i, *wxRED);
449 }
450 mFileListCtrl->SetItem(i, 1, Internat::FormatSize(byteCount).Translation());
451 mFileListCtrl->SetItemData(i, long(bOriginalExists));
452
453 ++i;
454 }
455
456 auto msg = kStdMsg();
459 mMessageStaticText->SetLabel(msg.Translation());
460
463}
464
465void DependencyDialog::OnList(wxListEvent &evt)
466{
468 return;
469
470 wxString itemStr = evt.GetText();
471 if (evt.GetData() == 0)
472 // This list item is one of mAliasedFiles for which
473 // the original is missing, i.e., moved or deleted.
474 // wxListCtrl does not provide for items that are not
475 // allowed to be selected, so always deselect these items.
476 mFileListCtrl->SetItemState(evt.GetIndex(), 0, wxLIST_STATE_SELECTED); // Deselect.
477
479 mFileListCtrl->GetSelectedItemCount() > 0);
480}
481
482void DependencyDialog::OnSize(wxSizeEvent &evt)
483{
484 int fileListCtrlWidth, fileListCtrlHeight;
485 mFileListCtrl->GetSize(&fileListCtrlWidth, &fileListCtrlHeight);
486
487 // File path is column 0. File size is column 1.
488 // File size column is always 120 px wide.
489 // Also subtract 8 from file path column width for borders.
490 mFileListCtrl->SetColumnWidth(0, fileListCtrlWidth - 120 - 8);
491 mFileListCtrl->SetColumnWidth(1, 120);
493}
494
495void DependencyDialog::OnNo(wxCommandEvent & WXUNUSED(event))
496{
498 EndModal(wxID_NO);
499}
500
501void DependencyDialog::OnYes(wxCommandEvent & WXUNUSED(event))
502{
504 EndModal(wxID_YES);
505}
506
507void DependencyDialog::OnCopySelectedFiles(wxCommandEvent & WXUNUSED(event))
508{
509 AliasedFileArray aliasedFilesToDelete, remainingAliasedFiles;
510
511 long i = 0;
512 for( const auto &file : mAliasedFiles ) {
513 if (mFileListCtrl->GetItemState(i, wxLIST_STATE_SELECTED))
514 aliasedFilesToDelete.push_back( file );
515 else
516 remainingAliasedFiles.push_back( file );
517 ++i;
518 }
519
520 // provides STRONG-GUARANTEE
521 RemoveDependencies(mProject, aliasedFilesToDelete);
522
523 // COMMIT OPERATIONS needing NOFAIL-GUARANTEE:
524 mAliasedFiles.swap( remainingAliasedFiles );
525 PopulateList();
526
527 if (mAliasedFiles.empty() || !mHasNonMissingFiles)
528 {
530 EndModal(wxID_NO); // Don't need to remove dependencies
531 }
532}
533
534void DependencyDialog::OnRightClick( wxListEvent& event)
535{
536 static_cast<void>(event);
537 wxMenu menu;
538 menu.Append(CopyNamesToClipboardID, _("&Copy Names to Clipboard"));
539 PopupMenu(&menu);
540}
541
543{
544 TranslatableString Files;
545 for (const auto &aliasedFile : mAliasedFiles) {
546 const wxFileName & fileName = aliasedFile.mFileName;
547 wxLongLong byteCount = (aliasedFile.mByteCount * 124) / 100;
548 bool bOriginalExists = aliasedFile.mbOriginalExists;
549 // All fields quoted, as e.g. size may contain a comma in the number.
550 Files += XO( "\"%s\", \"%s\", \"%s\"\n").Format(
551 fileName.GetFullPath(),
552 Internat::FormatSize( byteCount),
553 bOriginalExists ? XO("OK") : XO("Missing") );
554 }
555
556 // copy data onto clipboard
557 if (wxTheClipboard->Open()) {
558 // Clipboard owns the data you give it
559 wxTheClipboard->SetData(safenew wxTextDataObject(Files.Translation()));
560 wxTheClipboard->Close();
561 }
562}
563
564void DependencyDialog::OnCancel(wxCommandEvent& WXUNUSED(event))
565{
566 if (mIsSaving)
567 {
568 int ret = AudacityMessageBox(
569 XO(
570"If you proceed, your project will not be saved to disk. Is this what you want?"),
571 XO("Cancel Save"),
572 wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT, this);
573 if (ret != wxYES)
574 return;
575 }
576
577 EndModal(wxID_CANCEL);
578}
579
581{
583 {
584 wxString savePref;
585 int sel = mFutureActionChoice->GetSelection();
586 switch (sel)
587 {
588 case 1: savePref = wxT("copy"); break;
589 case 2: savePref = wxT("never"); break;
590 default: savePref = wxT("ask");
591 }
592 FileFormatsSaveWithDependenciesSetting.Write( savePref );
593 gPrefs->Flush();
594 }
595}
596
597// Checks for alias block files, modifies the project if the
598// user requests it, and returns true if the user continues.
599// Returns false only if the user clicks Cancel.
601 bool isSaving)
602{
603 auto pWindow = FindProjectFrame( project );
604 AliasedFileArray aliasedFiles;
605 FindDependencies(project, aliasedFiles);
606
607 if (aliasedFiles.empty()) {
608 if (!isSaving)
609 {
610 auto msg =
611XO("Your project is self-contained; it does not depend on any external audio files. \
612\n\nSome older Audacity projects may not be self-contained, and care \n\
613is needed to keep their external dependencies in the right place.\n\
614New projects will be self-contained and are less risky.");
616 msg,
617 XO("Dependency Check"),
618 wxOK | wxICON_INFORMATION,
619 pWindow);
620 }
621 return true; // Nothing to do.
622 }
623
624 if (isSaving)
625 {
626 RemoveDependencies(project, aliasedFiles);
627 return true;
628 }
629
630 DependencyDialog dlog(pWindow, -1, project, aliasedFiles, isSaving);
631 int returnCode = dlog.ShowModal();
632 if (returnCode == wxID_CANCEL)
633 return false;
634 else if (returnCode == wxID_YES)
635 RemoveDependencies(project, aliasedFiles);
636
637 return true;
638}
639
EVT_MENU(OnSetPlayRegionToSelectionID, AdornedRulerPanel::OnSetPlayRegionToSelection) EVT_COMMAND(OnTogglePinnedStateID
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
END_EVENT_TABLE()
static const TranslatableString kStdMsg()
std::unordered_map< BlockFile *, bool > BoolBlockFileHash
static const TranslatableString kExtraMsgForMissingFiles()
static void RemoveDependencies(AudacityProject *project, AliasedFileArray &aliasedFiles)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
bool ShowDependencyDialogIfNeeded(AudacityProject *project, bool isSaving)
static void GetAllSeqBlocks(AudacityProject *project, BlockPtrArray *outBlocks)
void FindDependencies(AudacityProject *project, AliasedFileArray &outAliasedFiles)
@ CopySelectedFilesButtonID
@ FutureActionChoiceID
@ FileListID
@ CopyNamesToClipboardID
std::unordered_map< wxString, AliasedFile * > AliasedFileHash
static void ReplaceBlockFiles(BlockPtrArray &blocks, ReplacedBlockFileHash &hash)
std::unordered_map< BlockFile *, BlockFilePtr > ReplacedBlockFileHash
std::list< AliasedFile > AliasedFileArray
Definition: Dependencies.h:52
EVT_LIST_ITEM_SELECTED(CurvesListID, EqualizationCurvesDialog::OnListSelectionChange) EVT_LIST_ITEM_DESELECTED(CurvesListID
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:9
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null,...
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
std::vector< SeqBlock * > BlockPtrArray
Definition: Sequence.h:51
@ eIsCreating
Definition: ShuttleGui.h:37
const auto project
#define S(N)
Definition: ToChars.cpp:64
static void OnSize(wxSizeEvent &evt)
Definition: VSTEditor.cpp:224
An audio file that is referenced (pointed into) directly from an Audacity .aup file rather than Audac...
Definition: Dependencies.h:24
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
DependencyDialog shows dependencies of an AudacityProject on AliasedFile s.
void OnList(wxListEvent &evt)
void OnCopyToClipboard(wxCommandEvent &evt)
wxListCtrl * mFileListCtrl
wxChoice * mFutureActionChoice
void OnYes(wxCommandEvent &evt)
void OnRightClick(wxListEvent &evt)
AudacityProject * mProject
wxButton * mCopyAllFilesButton
void OnCancel(wxCommandEvent &evt)
DependencyDialog(wxWindow *parent, wxWindowID id, AudacityProject *project, AliasedFileArray &aliasedFiles, bool isSaving)
AliasedFileArray & mAliasedFiles
void PopulateOrExchange(ShuttleGui &S)
wxButton * mCopySelectedFilesButton
wxStaticText * mMessageStaticText
void OnNo(wxCommandEvent &evt)
void OnSize(wxSizeEvent &evt)
void OnCopySelectedFiles(wxCommandEvent &evt)
void SaveFutureActionChoice()
static TranslatableString FormatSize(wxLongLong size)
Convert a number to a string while formatting it in bytes, KB, MB, GB.
Definition: Internat.cpp:179
ProgressDialog Class.
ProgressResult Update(int value, const TranslatableString &message={})
samplePtr ptr() const
Definition: SampleFormat.h:165
A WaveTrack contains WaveClip(s). A WaveClip contains a Sequence. A Sequence is primarily an interfac...
Definition: Sequence.h:53
BlockArray & GetBlockArray()
Definition: Sequence.h:218
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:630
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1097
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const
A Track that contains audio waveform data.
Definition: WaveTrack.h:227
virtual bool Flush() noexcept=0
Services * Get()
Fetch the global instance, or nullptr if none is yet installed.
Definition: BasicUI.cpp:196
PROJECT_RATE_API sampleFormat SampleFormatChoice()
constexpr auto Project
Return a tuple of values initialized from a subsequence of a tuple.
Definition: Tuple.h:77