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