Audacity  3.0.3
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 
100 static const auto exts = {
101  wxT("lof")
102 };
103 
104 class LOFImportPlugin final : public ImportPlugin
105 {
106 public:
108  : ImportPlugin( FileExtensions( exts.begin(), exts.end() ) )
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 {
123 public:
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 
144 private:
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
155  int nFilesInGroup{ 0 };
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 
180 std::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 
380  if (Internat::CompatibleToDouble(tokenholder, &scrollOffset))
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 
403  if (Internat::CompatibleToDouble(tokenholder, &durationFactor))
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  {
428  nFilesInGroup++;
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  {
449  mProject = DoImportMIDIProject(mProject, targetfile);
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 
552  if (callDurationFactor)
553  {
554  double longestDuration = TrackList::Get( *mProject ).GetEndTime();
555  ProjectWindow::Get( *mProject ).ZoomBy(longestDuration / durationFactor);
556  callDurationFactor = false;
557  }
558 
559  if (callScrollOffset)
560  {
562  callScrollOffset = false;
563  }
564 
565  if (doSomething)
566  // Amend last undo state
567  ProjectHistory::Get( *mProject ).ModifyState(false);
568 }
569 
571 {
572 }
ImportMIDI.h
LOFImportFileHandle::mTextFile
std::unique_ptr< wxTextFile > mTextFile
Definition: ImportLOF.cpp:149
ProjectHistory::ModifyState
void ModifyState(bool bWantsAutoSave)
Definition: ProjectHistory.cpp:124
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
TrackHolders
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
BasicUI::ProgressResult::Success
@ Success
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
Definition: AudacityMessageBox.cpp:17
LOFImportFileHandle::mLOFFileName
wxFileName mLOFFileName
Definition: ImportLOF.cpp:150
LOFImportFileHandle::GetFileDescription
TranslatableString GetFileDescription() override
Definition: ImportLOF.cpp:262
TranslatableStrings
std::vector< TranslatableString > TranslatableStrings
Definition: TranslatableString.h:295
LOFImportPlugin::GetPluginStringID
wxString GetPluginStringID() override
Definition: ImportLOF.cpp:114
LOFImportFileHandle::callScrollOffset
bool callScrollOffset
Definition: ImportLOF.cpp:162
ImportPlugin
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
Project.h
str
#define str(a)
Definition: DBConnection.cpp:30
Import.h
LOFImportFileHandle::~LOFImportFileHandle
~LOFImportFileHandle()
Definition: ImportLOF.cpp:570
Tags
ID3 Tags (for MP3)
Definition: Tags.h:74
ProjectWindow::TP_ScrollWindow
void TP_ScrollWindow(double scrollto) override
Definition: ProjectWindow.cpp:969
TrackList::Channels
static auto Channels(TrackType *pTrack) -> TrackIterRange< TrackType >
Definition: Track.h:1484
BasicUI::ProgressResult
ProgressResult
Definition: BasicUI.h:145
LOFImportFileHandle::doDurationAndScrollOffset
void doDurationAndScrollOffset()
Definition: ImportLOF.cpp:544
XO
#define XO(s)
Definition: Internat.h:31
ProjectWindow::Get
static ProjectWindow & Get(AudacityProject &project)
Definition: ProjectWindow.cpp:535
LOFImportPlugin::LOFImportPlugin
LOFImportPlugin()
Definition: ImportLOF.cpp:107
ProjectManager::OpenProject
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.
Definition: ProjectManager.cpp:949
exts
static const auto exts
Definition: ImportLOF.cpp:100
ProjectWindow::ZoomBy
void ZoomBy(double multiplier)
Definition: ProjectWindow.cpp:1590
wxArrayStringEx
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
Definition: wxArrayStringEx.h:18
LOFImportFileHandle::GetStreamCount
wxInt32 GetStreamCount() override
Definition: ImportLOF.cpp:133
LOFImportFileHandle::Import
ProgressResult Import(WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
Definition: ImportLOF.cpp:272
LOFImportPlugin::~LOFImportPlugin
~LOFImportPlugin()
Definition: ImportLOF.cpp:112
LOFImportFileHandle::SetStreamUsage
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportLOF.cpp:141
TrackList::GetEndTime
double GetEndTime() const
Definition: Track.cpp:1038
FileNames::IsMidi
FILES_API bool IsMidi(const FilePath &fName)
LOFImportFileHandle::durationFactor
double durationFactor
Definition: ImportLOF.cpp:159
FilePath
wxString FilePath
Definition: Project.h:20
LOFImportFileHandle::nFilesInGroup
int nFilesInGroup
Definition: ImportLOF.cpp:155
BINARY_FILE_CHECK_BUFFER_SIZE
#define BINARY_FILE_CHECK_BUFFER_SIZE
Definition: ImportLOF.cpp:96
LOFImportPlugin
An ImportPlugin for LOF data.
Definition: ImportLOF.cpp:105
name
const TranslatableString name
Definition: Distortion.cpp:98
ProjectManager::New
static AudacityProject * New()
Definition: ProjectManager.cpp:529
LOFImportFileHandle::lofOpenFiles
void lofOpenFiles(wxString *ln)
Processes a single line from a LOF text file, doing whatever is indicated on the line.
Definition: ImportLOF.cpp:349
DoImportMIDIProject
static AudacityProject * DoImportMIDIProject(AudacityProject *pProject, const FilePath &fileName)
Definition: ImportLOF.cpp:325
LOFImportFileHandle::scrollOffset
double scrollOffset
Definition: ImportLOF.cpp:163
ImportFileHandle
An ImportFileHandle for data.
Definition: ImportPlugin.h:107
LOFImportFileHandle::GetFileUncompressedBytes
ByteCount GetFileUncompressedBytes() override
Definition: ImportLOF.cpp:267
LOFImportFileHandle::mProject
AudacityProject * mProject
Definition: ImportLOF.cpp:152
WaveTrackFactory
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:713
LOFImportFileHandle::LOFImportFileHandle
LOFImportFileHandle(AudacityProject *pProject, const FilePath &name, std::unique_ptr< wxTextFile > &&file)
Definition: ImportLOF.cpp:166
LOFImportFileHandle
An ImportFileHandle for LOF data.
Definition: ImportLOF.cpp:122
registered
static Importer::RegisteredImportPlugin registered
Definition: ImportLOF.cpp:318
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:506
LOFImportPlugin::Open
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *pProject) override
Definition: ImportLOF.cpp:180
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:92
ImportPlugin.h
The interface that all file import "plugins" (if you want to call them that) must implement....
LOFImportFileHandle::callDurationFactor
bool callDurationFactor
Definition: ImportLOF.cpp:158
FileNames.h
Importer::RegisteredImportPlugin
Definition: Import.h:85
GetProjectFrame
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 ...
Definition: ProjectWindows.cpp:72
LOFImportPlugin::GetPluginFormatDescription
TranslatableString GetPluginFormatDescription() override
Definition: ImportLOF.cpp:175
Prefs.h
ImportFileHandle::ByteCount
unsigned long long ByteCount
Definition: ImportPlugin.h:127
DESC
#define DESC
Definition: ImportLOF.cpp:98
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
Internat::CompatibleToDouble
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:134
LOFImportFileHandle::GetStreamInfo
const TranslatableStrings & GetStreamInfo() override
Definition: ImportLOF.cpp:135
DoImportMIDI
bool DoImportMIDI(AudacityProject &project, const FilePath &fileName)
Definition: ImportMIDI.cpp:37