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