Audacity 3.2.0
ImportLOF.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ImportLOF.h
6
7 David I. Murray
8 Leland Lucius
9
10*//****************************************************************//****************************************************************//*******************************************************************/
71
72#include <wx/frame.h>
73#include <wx/textfile.h>
74#include <wx/tokenzr.h>
75
76#include "FileNames.h"
77#include "WaveTrack.h"
78#include "ImportPlugin.h"
80#include "Import.h"
81#include "Project.h"
82#include "ProjectHistory.h"
83#include "ProjectManager.h"
84#include "Viewport.h"
85#include "ProjectWindows.h"
86#include "ImportUtils.h"
87
88#define BINARY_FILE_CHECK_BUFFER_SIZE 1024
89
90#define DESC XO("List of Files in basic text format")
91
92static const auto exts = {
93 wxT("lof")
94};
95
96class LOFImportPlugin final : public ImportPlugin
97{
98public:
101 {
102 }
103
105
106 wxString GetPluginStringID() override { return wxT("lof"); }
108 std::unique_ptr<ImportFileHandle> Open(
109 const FilePath &Filename, AudacityProject *pProject) override;
110};
111
112
114{
115public:
117 const FilePath & name, std::unique_ptr<wxTextFile> &&file);
119
122 void Import(
123 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
124 TrackHolders& outTracks, Tags* tags,
125 std::optional<LibFileFormats::AcidizerTags>& outAcidTags) override;
126
127 FilePath GetFilename() const override;
128
129 void Cancel() override;
130
131 void Stop() override;
132
133 wxInt32 GetStreamCount() override { return 1; }
134
136 {
137 static TranslatableStrings empty;
138 return empty;
139 }
140
141 void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
142 {}
143
144private:
145 // Takes a line of text in lof file and interprets it and opens files
146 void lofOpenFiles(wxString* ln);
148
149 std::unique_ptr<wxTextFile> mTextFile;
150 const wxFileName mLOFFileName;
153
154 // In order to know whether or not to create a NEW window
156
157 // In order to zoom in, it must be done after files are opened
158 bool callDurationFactor{ false };
159 double durationFactor{ 1 };
160
161 // In order to offset scrollbar, it must be done after files are opened
162 bool callScrollOffset{ false };
163 double scrollOffset{ 0 };
164};
165
167 const FilePath & name, std::unique_ptr<wxTextFile> &&file)
168: mTextFile(std::move(file))
169 , mLOFFileName{name}
170 , mProject{ pProject }
171{
172}
173
175{
176 return DESC;
177}
178
179std::unique_ptr<ImportFileHandle> LOFImportPlugin::Open(
180 const FilePath &filename, AudacityProject *pProject)
181{
182 // Check if it is a text file.
183 {
184 wxFile binaryFile;
185 if (!binaryFile.Open(filename))
186 return nullptr; // File not found
187
189 int count = binaryFile.Read(buf, BINARY_FILE_CHECK_BUFFER_SIZE);
190
191 bool isTextFile = false;
192 const std::string lofToken("file");
193
194 // At least we should get a size more than: <token> + <space> + <filename>.
195 if (count > (lofToken.length() + sizeof(' ') + 1))
196 {
197 // Audacity can import list from LOF only with BOM or ASCII,
198 // each other text (like unicode without BOM, bin, etc) can't be recognized.
199
200 // UTF-16 BOM checker.
201 auto IsUtf16_BE = [](const char* str) -> bool
202 {
203 return str[0] == static_cast<char>(0xFE) && str[1] == static_cast<char>(0xFF);
204 };
205 auto IsUtf16_LE = [](const char* str) -> bool
206 {
207 return str[0] == static_cast<char>(0xFF) && str[1] == static_cast<char>(0xFE);
208 };
209
210 // UTF-32 BOM checker.
211 auto IsUtf32_BE = [](const char* str) -> bool
212 {
213 return str[0] == static_cast<char>(0x00) &&
214 str[1] == static_cast<char>(0x00) &&
215 str[2] == static_cast<char>(0xFE) &&
216 str[3] == static_cast<char>(0xFF);
217 };
218 auto IsUtf32_LE = [](const char* str) -> bool
219 {
220 return str[0] == static_cast<char>(0xFF) &&
221 str[1] == static_cast<char>(0xFE) &&
222 str[2] == static_cast<char>(0x00) &&
223 str[3] == static_cast<char>(0x00);
224 };
225
226 // Is unicode text file.
227 if (IsUtf16_BE(buf) || IsUtf16_LE(buf) || IsUtf32_BE(buf) || IsUtf32_LE(buf))
228 {
229 isTextFile = true;
230 }
231 // Try parse as ASCII and UTF-8 text as ASCII too.
232 else
233 {
234 buf[sizeof(buf) - 1] = '\0';
235
236 std::string importedText(buf);
237
238 if (importedText.find(lofToken) != std::string::npos)
239 isTextFile = true;
240 }
241 }
242
243 if (!isTextFile)
244 {
245 binaryFile.Close();
246 return nullptr;
247 }
248 }
249
250 // Now open the file again as text file
251 auto file = std::make_unique<wxTextFile>(filename);
252 file->Open();
253
254 if (!file->IsOpened())
255 return nullptr;
256
257 return std::make_unique<LOFImportFileHandle>(
258 pProject, filename, std::move(file));
259}
260
262{
263 return DESC;
264}
265
267{
268 return 0;
269}
270
272 ImportProgressListener& progressListener, WaveTrackFactory*,
273 TrackHolders& outTracks, Tags*, std::optional<LibFileFormats::AcidizerTags>&)
274{
275 // Unlike other ImportFileHandle subclasses, this one never gives any tracks
276 // back to the caller.
277 // Instead, it recursively calls AudacityProject::Import for each file listed
278 // in the .lof file.
279 // Each importation creates a NEW undo state.
280 // If there is an error or exception during one of them, only that one's
281 // side effects are rolled back, and the rest of the import list is skipped.
282 // The file may have "window" directives that cause NEW AudacityProjects
283 // to be created, and the undo states are pushed onto the latest project.
284 // If a project is created but the first file import into it fails, destroy
285 // the project.
286
287 outTracks.clear();
288
289 wxASSERT(mTextFile->IsOpened());
290
291 if(mTextFile->Eof())
292 {
293 mTextFile->Close();
295 return;
296 }
297
298 wxString line = mTextFile->GetFirstLine();
299
300 while (!mTextFile->Eof())
301 {
302 lofOpenFiles(&line);
303 line = mTextFile->GetNextLine();
304 }
305
306 // for last line
307 lofOpenFiles(&line);
308
309 if(!mTextFile->Close())
310 {
312 return;
313 }
314 // set any duration/offset factors for last window, as all files were called
317}
318
320{
321 return mLOFFileName.GetFullPath();
322}
323
325{
326 //LOFImport delegates import to other plugins
327}
328
330{
331 //LOFImport delegates import to other plugins
332}
333
335 std::make_unique< LOFImportPlugin >()
336};
337
345{
346 wxStringTokenizer tok(*ln, wxT(" "));
347 wxStringTokenizer temptok1(*ln, wxT("\""));
348 wxStringTokenizer temptok2(*ln, wxT(" "));
349 int tokenplace = 0;
350
351 wxString targetfile;
352 wxString tokenholder = tok.GetNextToken();
353
354
355 if (tokenholder.IsSameAs(wxT("window"), false))
356 {
357 // set any duration/offset factors for last window, as all files were called
359
360 if (nFilesInGroup > 0 )
361 // Cause a project to be created with the next import
362 mProject = nullptr;
363
364 nFilesInGroup = 0;
365
366 while (tok.HasMoreTokens())
367 {
368 tokenholder = tok.GetNextToken();
369
370 if (tokenholder.IsSameAs(wxT("offset"), false))
371 {
372 if (tok.HasMoreTokens())
373 tokenholder = tok.GetNextToken();
374
376 {
377 callScrollOffset = true;
378 }
379 else
380 {
382 /* i18n-hint: You do not need to translate "LOF" */
383 XO("Invalid window offset in LOF file."));
384 }
385
386 if (tok.HasMoreTokens())
387 tokenholder = tok.GetNextToken();
388 }
389
390 if (tokenholder.IsSameAs(wxT("duration"), false))
391 {
392 if (tok.HasMoreTokens())
393 tokenholder = tok.GetNextToken();
394
396 {
397 callDurationFactor = true;
398 }
399 else
400 {
402 /* i18n-hint: You do not need to translate "LOF" */
403 XO("Invalid duration in LOF file."));
404 }
405 } // End if statement
406
407 if (tokenholder == wxT("#"))
408 {
409 // # indicates comments; ignore line
410 tok = wxStringTokenizer(wxT(""), wxT(" "));
411 }
412 } // End while loop
413 } // End if statement handling "window" lines
414
415 else if (tokenholder.IsSameAs(wxT("file"), false))
416 {
418 // To identify filename and open it
419 tokenholder = temptok1.GetNextToken();
420 wxString targettoken = temptok1.GetNextToken();
421 targetfile = targettoken;
422
423 // If path is relative, make absolute path from LOF path
424 if(!wxIsAbsolutePath(targetfile)) {
425 wxFileName fName(targetfile);
426 fName.Normalize(wxPATH_NORM_ALL, mLOFFileName.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
427 if(fName.FileExists()) {
428 targetfile = fName.GetFullPath();
429 }
430 }
431
432 // Do recursive call to import
434 true /* addtohistory */, true /* reuseNonemptyProject */ );
435
436 // Set tok to right after filename
437 temptok2.SetString(targettoken);
438 tokenplace = temptok2.CountTokens();
439
440 for (int i = 0; i < tokenplace; i++)
441 tokenholder = tok.GetNextToken();
442
443 if (tok.HasMoreTokens())
444 {
445 tokenholder = tok.GetNextToken();
446
447 if (tokenholder == wxT("#"))
448 {
449 // # indicates comments; ignore line
450 tok = wxStringTokenizer(wxT(""), wxT(" "));
451 }
452
453 if (tokenholder.IsSameAs(wxT("offset"), false))
454 {
455 if (tok.HasMoreTokens())
456 tokenholder = tok.GetNextToken();
457 double offset;
458
459 // handle an "offset" specifier
460 if (!mProject)
461 // there was an import error,
462 // presumably with its own error message
463 ;
464 else if (Internat::CompatibleToDouble(tokenholder, &offset))
465 {
466 auto &tracks = TrackList::Get( *mProject );
467 auto t = *tracks.rbegin();
468
469 // t is now the last track in the project, unless the import of
470 // all tracks failed, in which case it will be null. In that
471 // case we return because we cannot offset a non-existent track.
472 if (t == NULL)
473 return;
474#ifdef USE_MIDI
475 if (targetfile.AfterLast(wxT('.')).IsSameAs(wxT("mid"), false) ||
476 targetfile.AfterLast(wxT('.')).IsSameAs(wxT("midi"), false))
477 {
478 ImportUtils::ShowMessageBox(XO("MIDI tracks cannot be offset individually, only audio files can be."));
479 }
480 else
481#endif
482 t->MoveTo(offset);
483
484 // Amend the undo transaction made by import
485 ProjectHistory::Get( *mProject ).ModifyState(false);
486 } // end of converting "offset" argument
487 else
488 {
490 /* i18n-hint: You do not need to translate "LOF" */
491 XO("Invalid track offset in LOF file."));
492 }
493 } // End if statement for "offset" parameters
494 } // End if statement (more tokens after file name)
495 } // End if statement "file" lines
496
497 else if (tokenholder == wxT("#"))
498 {
499 // # indicates comments; ignore line
500 tok = wxStringTokenizer(wxT(""), wxT(" "));
501 }
502 else
503 {
504 // Couldn't parse a line
505 }
506}
507
509{
510 if (!mProject)
511 return;
512
514 bool doSomething = callDurationFactor || callScrollOffset;
515
517 {
518 double longestDuration = TrackList::Get(*mProject).GetEndTime();
519 Viewport::Get(*mProject).ZoomBy(longestDuration / durationFactor);
520 callDurationFactor = false;
521 }
522
524 {
526 callScrollOffset = false;
527 }
528
529 if (doSomething)
530 // Amend last undo state
531 ProjectHistory::Get( *mProject ).ModifyState(false);
532}
533
535{
536}
wxT("CloseDown"))
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
static const auto exts
Definition: ImportLOF.cpp:92
#define DESC
Definition: ImportLOF.cpp:90
static Importer::RegisteredImportPlugin registered
Definition: ImportLOF.cpp:334
#define BINARY_FILE_CHECK_BUFFER_SIZE
Definition: ImportLOF.cpp:88
The interface that all file import "plugins" (if you want to call them that) must implement....
std::vector< std::shared_ptr< TrackList > > TrackHolders
Definition: ImportRaw.h:24
wxString FilePath
Definition: Project.h:21
accessors for certain important windows associated with each project
const auto tracks
std::vector< TranslatableString > TranslatableStrings
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
Base class for FlacImportFileHandle, LOFImportFileHandle, MP3ImportFileHandle, OggImportFileHandle an...
Definition: ImportPlugin.h:111
unsigned long long ByteCount
Definition: ImportPlugin.h:114
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
Interface used to report on import state and progress.
virtual void OnImportResult(ImportResult result)=0
Used to report on import result for file handle passed as argument to OnImportFileOpened.
static void ShowMessageBox(const TranslatableString &message, const TranslatableString &caption=XO("Import Project"))
Definition: ImportUtils.cpp:43
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:109
An ImportFileHandle for LOF data.
Definition: ImportLOF.cpp:114
const TranslatableStrings & GetStreamInfo() override
Definition: ImportLOF.cpp:135
const wxFileName mLOFFileName
Definition: ImportLOF.cpp:150
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportLOF.cpp:141
void lofOpenFiles(wxString *ln)
Processes a single line from a LOF text file, doing whatever is indicated on the line.
Definition: ImportLOF.cpp:344
LOFImportFileHandle(AudacityProject *pProject, const FilePath &name, std::unique_ptr< wxTextFile > &&file)
Definition: ImportLOF.cpp:166
ByteCount GetFileUncompressedBytes() override
Definition: ImportLOF.cpp:266
void Stop() override
Definition: ImportLOF.cpp:329
FilePath GetFilename() const override
Definition: ImportLOF.cpp:319
std::unique_ptr< wxTextFile > mTextFile
Definition: ImportLOF.cpp:149
void Cancel() override
Definition: ImportLOF.cpp:324
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags, std::optional< LibFileFormats::AcidizerTags > &outAcidTags) override
Definition: ImportLOF.cpp:271
wxInt32 GetStreamCount() override
Definition: ImportLOF.cpp:133
AudacityProject * mProject
Definition: ImportLOF.cpp:152
void doDurationAndScrollOffset()
Definition: ImportLOF.cpp:508
TranslatableString GetFileDescription() override
Definition: ImportLOF.cpp:261
An ImportPlugin for LOF data.
Definition: ImportLOF.cpp:97
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *pProject) override
Definition: ImportLOF.cpp:179
TranslatableString GetPluginFormatDescription() override
Definition: ImportLOF.cpp:174
wxString GetPluginStringID() override
Definition: ImportLOF.cpp:106
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static AudacityProject * OpenProject(AudacityProject *pGivenProject, const FilePath &fileNameArg, bool addtohistory, bool reuseNonemptyProject)
Open a file into an AudacityProject, returning the project, or nullptr for failure.
ID3 Tags (for MP3)
Definition: Tags.h:73
double GetEndTime() const
Return the greatest end time of the tracks, or 0 when no tracks.
Definition: Track.cpp:991
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:347
Holds a msgid for the translation catalog; may also bind format arguments.
void ZoomBy(double multiplier)
Multiply the magnification; unchanged left edge time.
Definition: Viewport.cpp:510
void SetHorizontalThumb(double scrollto, bool doScroll=true)
Definition: Viewport.cpp:200
static Viewport & Get(AudacityProject &project)
Definition: Viewport.cpp:32
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:1279
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
auto begin(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:150
constexpr size_t npos(-1)
STL namespace.