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