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
73
74#include <wx/textfile.h>
75#include <wx/tokenzr.h>
76
77#ifdef USE_MIDI
78#include "import/ImportMIDI.h"
79#endif // USE_MIDI
80#include "FileNames.h"
81#include "WaveTrack.h"
82#include "ImportPlugin.h"
84#include "Import.h"
85#include "Project.h"
86#include "ProjectHistory.h"
87#include "ProjectManager.h"
88#include "ProjectWindow.h"
89#include "ProjectWindows.h"
90#include "ImportUtils.h"
91
92#define BINARY_FILE_CHECK_BUFFER_SIZE 1024
93
94#define DESC XO("List of Files in basic text format")
95
96static const auto exts = {
97 wxT("lof")
98};
99
100class LOFImportPlugin final : public ImportPlugin
101{
102public:
105 {
106 }
107
109
110 wxString GetPluginStringID() override { return wxT("lof"); }
112 std::unique_ptr<ImportFileHandle> Open(
113 const FilePath &Filename, AudacityProject *pProject) override;
114};
115
116
118{
119public:
121 const FilePath & name, std::unique_ptr<wxTextFile> &&file);
123
126 void Import(ImportProgressListener& progressListener,
127 WaveTrackFactory *trackFactory,
128 TrackHolders &outTracks,
129 Tags *tags) override;
130
131 FilePath GetFilename() const override;
132
133 void Cancel() override;
134
135 void Stop() override;
136
137 wxInt32 GetStreamCount() override { return 1; }
138
140 {
141 static TranslatableStrings empty;
142 return empty;
143 }
144
145 void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
146 {}
147
148private:
149 // Takes a line of text in lof file and interprets it and opens files
150 void lofOpenFiles(wxString* ln);
152
153 std::unique_ptr<wxTextFile> mTextFile;
154 const wxFileName mLOFFileName;
157
158 // In order to know whether or not to create a NEW window
160
161 // In order to zoom in, it must be done after files are opened
162 bool callDurationFactor{ false };
163 double durationFactor{ 1 };
164
165 // In order to offset scrollbar, it must be done after files are opened
166 bool callScrollOffset{ false };
167 double scrollOffset{ 0 };
168};
169
171 const FilePath & name, std::unique_ptr<wxTextFile> &&file)
172: mTextFile(std::move(file))
173 , mLOFFileName{name}
174 , mProject{ pProject }
175{
176}
177
179{
180 return DESC;
181}
182
183std::unique_ptr<ImportFileHandle> LOFImportPlugin::Open(
184 const FilePath &filename, AudacityProject *pProject)
185{
186 // Check if it is a text file.
187 {
188 wxFile binaryFile;
189 if (!binaryFile.Open(filename))
190 return nullptr; // File not found
191
193 int count = binaryFile.Read(buf, BINARY_FILE_CHECK_BUFFER_SIZE);
194
195 bool isTextFile = false;
196 const std::string lofToken("file");
197
198 // At least we should get a size more than: <token> + <space> + <filename>.
199 if (count > (lofToken.length() + sizeof(' ') + 1))
200 {
201 // Audacity can import list from LOF only with BOM or ASCII,
202 // each other text (like unicode without BOM, bin, etc) can't be recognized.
203
204 // UTF-16 BOM checker.
205 auto IsUtf16_BE = [](const char* str) -> bool
206 {
207 return str[0] == static_cast<char>(0xFE) && str[1] == static_cast<char>(0xFF);
208 };
209 auto IsUtf16_LE = [](const char* str) -> bool
210 {
211 return str[0] == static_cast<char>(0xFF) && str[1] == static_cast<char>(0xFE);
212 };
213
214 // UTF-32 BOM checker.
215 auto IsUtf32_BE = [](const char* str) -> bool
216 {
217 return str[0] == static_cast<char>(0x00) &&
218 str[1] == static_cast<char>(0x00) &&
219 str[2] == static_cast<char>(0xFE) &&
220 str[3] == static_cast<char>(0xFF);
221 };
222 auto IsUtf32_LE = [](const char* str) -> bool
223 {
224 return str[0] == static_cast<char>(0xFF) &&
225 str[1] == static_cast<char>(0xFE) &&
226 str[2] == static_cast<char>(0x00) &&
227 str[3] == static_cast<char>(0x00);
228 };
229
230 // Is unicode text file.
231 if (IsUtf16_BE(buf) || IsUtf16_LE(buf) || IsUtf32_BE(buf) || IsUtf32_LE(buf))
232 {
233 isTextFile = true;
234 }
235 // Try parse as ASCII and UTF-8 text as ASCII too.
236 else
237 {
238 buf[sizeof(buf) - 1] = '\0';
239
240 std::string importedText(buf);
241
242 if (importedText.find(lofToken) != std::string::npos)
243 isTextFile = true;
244 }
245 }
246
247 if (!isTextFile)
248 {
249 binaryFile.Close();
250 return nullptr;
251 }
252 }
253
254 // Now open the file again as text file
255 auto file = std::make_unique<wxTextFile>(filename);
256 file->Open();
257
258 if (!file->IsOpened())
259 return nullptr;
260
261 return std::make_unique<LOFImportFileHandle>(
262 pProject, filename, std::move(file));
263}
264
266{
267 return DESC;
268}
269
271{
272 return 0;
273}
274
277 TrackHolders &outTracks,
278 Tags*)
279{
280 // Unlike other ImportFileHandle subclasses, this one never gives any tracks
281 // back to the caller.
282 // Instead, it recursively calls AudacityProject::Import for each file listed
283 // in the .lof file.
284 // Each importation creates a NEW undo state.
285 // If there is an error or exception during one of them, only that one's
286 // side effects are rolled back, and the rest of the import list is skipped.
287 // The file may have "window" directives that cause NEW AudacityProjects
288 // to be created, and the undo states are pushed onto the latest project.
289 // If a project is created but the first file import into it fails, destroy
290 // the project.
291
292 outTracks.clear();
293
294 wxASSERT(mTextFile->IsOpened());
295
296 if(mTextFile->Eof())
297 {
298 mTextFile->Close();
300 return;
301 }
302
303 wxString line = mTextFile->GetFirstLine();
304
305 while (!mTextFile->Eof())
306 {
307 lofOpenFiles(&line);
308 line = mTextFile->GetNextLine();
309 }
310
311 // for last line
312 lofOpenFiles(&line);
313
314 if(!mTextFile->Close())
315 {
317 return;
318 }
319 // set any duration/offset factors for last window, as all files were called
322}
323
325{
326 return mLOFFileName.GetFullPath();
327}
328
330{
331 //LOFImport delegates import to other plugins
332}
333
335{
336 //LOFImport delegates import to other plugins
337}
338
340 std::make_unique< LOFImportPlugin >()
341};
342
343#ifdef USE_MIDI
344// return null on failure; if success, return the given project, or a NEW
345// one, if the given was null; create no NEW project if failure
347 AudacityProject *pProject, const FilePath &fileName)
348{
349 AudacityProject *pNewProject {};
350 if ( !pProject )
351 pProject = pNewProject = ProjectManager::New();
352 auto cleanup = finally( [&]
353 { if ( pNewProject ) GetProjectFrame( *pNewProject ).Close(true); } );
354
355 if ( DoImportMIDI( *pProject, fileName ) ) {
356 pNewProject = nullptr;
357 return pProject;
358 }
359 else
360 return nullptr;
361}
362#endif
363
371{
372 wxStringTokenizer tok(*ln, wxT(" "));
373 wxStringTokenizer temptok1(*ln, wxT("\""));
374 wxStringTokenizer temptok2(*ln, wxT(" "));
375 int tokenplace = 0;
376
377 wxString targetfile;
378 wxString tokenholder = tok.GetNextToken();
379
380
381 if (tokenholder.IsSameAs(wxT("window"), false))
382 {
383 // set any duration/offset factors for last window, as all files were called
385
386 if (nFilesInGroup > 0 )
387 // Cause a project to be created with the next import
388 mProject = nullptr;
389
390 nFilesInGroup = 0;
391
392 while (tok.HasMoreTokens())
393 {
394 tokenholder = tok.GetNextToken();
395
396 if (tokenholder.IsSameAs(wxT("offset"), false))
397 {
398 if (tok.HasMoreTokens())
399 tokenholder = tok.GetNextToken();
400
402 {
403 callScrollOffset = true;
404 }
405 else
406 {
408 /* i18n-hint: You do not need to translate "LOF" */
409 XO("Invalid window offset in LOF file."));
410 }
411
412 if (tok.HasMoreTokens())
413 tokenholder = tok.GetNextToken();
414 }
415
416 if (tokenholder.IsSameAs(wxT("duration"), false))
417 {
418 if (tok.HasMoreTokens())
419 tokenholder = tok.GetNextToken();
420
422 {
423 callDurationFactor = true;
424 }
425 else
426 {
428 /* i18n-hint: You do not need to translate "LOF" */
429 XO("Invalid duration in LOF file."));
430 }
431 } // End if statement
432
433 if (tokenholder == wxT("#"))
434 {
435 // # indicates comments; ignore line
436 tok = wxStringTokenizer(wxT(""), wxT(" "));
437 }
438 } // End while loop
439 } // End if statement handling "window" lines
440
441 else if (tokenholder.IsSameAs(wxT("file"), false))
442 {
444 // To identify filename and open it
445 tokenholder = temptok1.GetNextToken();
446 wxString targettoken = temptok1.GetNextToken();
447 targetfile = targettoken;
448
449 // If path is relative, make absolute path from LOF path
450 if(!wxIsAbsolutePath(targetfile)) {
451 wxFileName fName(targetfile);
452 fName.Normalize(wxPATH_NORM_ALL, mLOFFileName.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
453 if(fName.FileExists()) {
454 targetfile = fName.GetFullPath();
455 }
456 }
457
458 // Do recursive call to import
459
460#ifdef USE_MIDI
461 // If file is a midi
462 if (FileNames::IsMidi(targetfile))
463 {
465 }
466
467 // If not a midi, open audio file
468 else
469
470#else // !USE_MIDI
471 /* if we don't have midi support, go straight on to opening as an
472 * audio file. TODO: Some sort of message here? */
473
474#endif // USE_MIDI
476 true /* addtohistory */, true /* reuseNonemptyProject */ );
477
478 // Set tok to right after filename
479 temptok2.SetString(targettoken);
480 tokenplace = temptok2.CountTokens();
481
482 for (int i = 0; i < tokenplace; i++)
483 tokenholder = tok.GetNextToken();
484
485 if (tok.HasMoreTokens())
486 {
487 tokenholder = tok.GetNextToken();
488
489 if (tokenholder == wxT("#"))
490 {
491 // # indicates comments; ignore line
492 tok = wxStringTokenizer(wxT(""), wxT(" "));
493 }
494
495 if (tokenholder.IsSameAs(wxT("offset"), false))
496 {
497 if (tok.HasMoreTokens())
498 tokenholder = tok.GetNextToken();
499 double offset;
500
501 // handle an "offset" specifier
502 if (!mProject)
503 // there was an import error,
504 // presumably with its own error message
505 ;
506 else if (Internat::CompatibleToDouble(tokenholder, &offset))
507 {
508 auto &tracks = TrackList::Get( *mProject );
509 auto t = *tracks.rbegin();
510
511 // t is now the last track in the project, unless the import of
512 // all tracks failed, in which case it will be null. In that
513 // case we return because we cannot offset a non-existent track.
514 if (t == NULL)
515 return;
516#ifdef USE_MIDI
517 if (targetfile.AfterLast(wxT('.')).IsSameAs(wxT("mid"), false) ||
518 targetfile.AfterLast(wxT('.')).IsSameAs(wxT("midi"), false))
519 {
520 ImportUtils::ShowMessageBox(XO("MIDI tracks cannot be offset individually, only audio files can be."));
521 }
522 else
523#endif
524 t->MoveTo(offset);
525
526 // Amend the undo transaction made by import
527 ProjectHistory::Get( *mProject ).ModifyState(false);
528 } // end of converting "offset" argument
529 else
530 {
532 /* i18n-hint: You do not need to translate "LOF" */
533 XO("Invalid track offset in LOF file."));
534 }
535 } // End if statement for "offset" parameters
536 } // End if statement (more tokens after file name)
537 } // End if statement "file" lines
538
539 else if (tokenholder == wxT("#"))
540 {
541 // # indicates comments; ignore line
542 tok = wxStringTokenizer(wxT(""), wxT(" "));
543 }
544 else
545 {
546 // Couldn't parse a line
547 }
548}
549
551{
552 if (!mProject)
553 return;
554
556 bool doSomething = callDurationFactor || callScrollOffset;
557
559 {
560 double longestDuration = TrackList::Get(*mProject).GetEndTime();
561 ProjectWindow::Get( *mProject ).ZoomBy(longestDuration / durationFactor);
562 callDurationFactor = false;
563 }
564
566 {
568 callScrollOffset = false;
569 }
570
571 if (doSomething)
572 // Amend last undo state
573 ProjectHistory::Get( *mProject ).ModifyState(false);
574}
575
577{
578}
wxT("CloseDown"))
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
XO("Cut/Copy/Paste")
static const auto exts
Definition: ImportLOF.cpp:96
#define DESC
Definition: ImportLOF.cpp:94
static Importer::RegisteredImportPlugin registered
Definition: ImportLOF.cpp:339
static AudacityProject * DoImportMIDIProject(AudacityProject *pProject, const FilePath &fileName)
Definition: ImportLOF.cpp:346
#define BINARY_FILE_CHECK_BUFFER_SIZE
Definition: ImportLOF.cpp:92
bool DoImportMIDI(AudacityProject &project, const FilePath &fileName)
Definition: ImportMIDI.cpp:39
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
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
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:42
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:133
An ImportFileHandle for LOF data.
Definition: ImportLOF.cpp:118
const TranslatableStrings & GetStreamInfo() override
Definition: ImportLOF.cpp:139
const wxFileName mLOFFileName
Definition: ImportLOF.cpp:154
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
Definition: ImportLOF.cpp:275
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportLOF.cpp:145
void lofOpenFiles(wxString *ln)
Processes a single line from a LOF text file, doing whatever is indicated on the line.
Definition: ImportLOF.cpp:370
LOFImportFileHandle(AudacityProject *pProject, const FilePath &name, std::unique_ptr< wxTextFile > &&file)
Definition: ImportLOF.cpp:170
ByteCount GetFileUncompressedBytes() override
Definition: ImportLOF.cpp:270
void Stop() override
Definition: ImportLOF.cpp:334
FilePath GetFilename() const override
Definition: ImportLOF.cpp:324
std::unique_ptr< wxTextFile > mTextFile
Definition: ImportLOF.cpp:153
void Cancel() override
Definition: ImportLOF.cpp:329
wxInt32 GetStreamCount() override
Definition: ImportLOF.cpp:137
AudacityProject * mProject
Definition: ImportLOF.cpp:156
void doDurationAndScrollOffset()
Definition: ImportLOF.cpp:550
TranslatableString GetFileDescription() override
Definition: ImportLOF.cpp:265
An ImportPlugin for LOF data.
Definition: ImportLOF.cpp:101
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *pProject) override
Definition: ImportLOF.cpp:183
TranslatableString GetPluginFormatDescription() override
Definition: ImportLOF.cpp:178
wxString GetPluginStringID() override
Definition: ImportLOF.cpp:110
void ModifyState(bool bWantsAutoSave)
static ProjectHistory & Get(AudacityProject &project)
static AudacityProject * New()
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.
void ZoomBy(double multiplier)
static ProjectWindow & Get(AudacityProject &project)
void TP_ScrollWindow(double scrollto) override
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:975
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:354
Holds a msgid for the translation catalog; may also bind format arguments.
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:1074
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
FILES_API bool IsMidi(const FilePath &fName)
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.