Audacity  2.2.2
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 "../Audacity.h"
73 #include "ImportLOF.h"
74 
75 #include <wx/string.h>
76 #include <wx/utils.h>
77 #include <wx/intl.h>
78 #include <wx/textfile.h>
79 #include <wx/tokenzr.h>
80 
81 #ifdef USE_MIDI
82 #include "ImportMIDI.h"
83 #endif // USE_MIDI
84 #include "../WaveTrack.h"
85 #include "ImportPlugin.h"
86 #include "Import.h"
87 #include "../NoteTrack.h"
88 #include "../Project.h"
89 #include "../FileFormats.h"
90 #include "../Prefs.h"
91 #include "../Internat.h"
92 #include "../widgets/ErrorDialog.h"
93 
94 #define BINARY_FILE_CHECK_BUFFER_SIZE 1024
95 
96 #define DESC _("List of Files in basic text format")
97 
98 static const wxChar *exts[] =
99 {
100  wxT("lof")
101 };
102 
103 class LOFImportPlugin final : public ImportPlugin
104 {
105 public:
107  : ImportPlugin(wxArrayString(WXSIZEOF(exts), exts))
108  {
109  }
110 
112 
113  wxString GetPluginStringID() { return wxT("lof"); }
114  wxString GetPluginFormatDescription();
115  std::unique_ptr<ImportFileHandle> Open(const wxString &Filename) override;
116 };
117 
118 
120 {
121 public:
122  LOFImportFileHandle(const wxString & name, std::unique_ptr<wxTextFile> &&file);
124 
125  wxString GetFileDescription() override;
127  ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks,
128  Tags *tags) override;
129 
130  wxInt32 GetStreamCount() override { return 1; }
131 
132  const wxArrayString &GetStreamInfo() override
133  {
134  static wxArrayString empty;
135  return empty;
136  }
137 
138  void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
139  {}
140 
141 private:
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
152  bool windowCalledOnce{ false };
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 wxString & name, std::unique_ptr<wxTextFile> &&file)
165 : ImportFileHandle(name),
166  mTextFile(std::move(file))
167  , mLOFFileName{name}
168 {
169 }
170 
171 void GetLOFImportPlugin(ImportPluginList &importPluginList,
172  UnusableImportPluginList & WXUNUSED(unusableImportPluginList))
173 {
174  importPluginList.push_back( make_movable<LOFImportPlugin>() );
175 }
176 
178 {
179  return DESC;
180 }
181 
182 std::unique_ptr<ImportFileHandle> LOFImportPlugin::Open(const wxString &filename)
183 {
184  // Check if it is a binary file
185  {
186  wxFile binaryFile;
187  if (!binaryFile.Open(filename))
188  return nullptr; // File not found
189 
191  int count = binaryFile.Read(buf, BINARY_FILE_CHECK_BUFFER_SIZE);
192 
193  for (int i = 0; i < count; i++)
194  {
195  // Check if this char is below the space character, but not a
196  // line feed or carriage return
197  if (buf[i] < 32 && buf[i] != 10 && buf[i] != 13)
198  {
199  // Assume it is a binary file
200  binaryFile.Close();
201  return nullptr;
202  }
203  }
204  }
205 
206  // Now open the file again as text file
207  auto file = std::make_unique<wxTextFile>(filename);
208  file->Open();
209 
210  if (!file->IsOpened())
211  return nullptr;
212 
213  return std::make_unique<LOFImportFileHandle>(filename, std::move(file));
214 }
215 
217 {
218  return DESC;
219 }
220 
222 {
223  return 0;
224 }
225 
227  Tags * WXUNUSED(tags))
228 {
229  // Unlike other ImportFileHandle subclasses, this one never gives any tracks
230  // back to the caller.
231  // Instead, it recursively calls AudacityProject::Import for each file listed
232  // in the .lof file.
233  // Each importation creates a NEW undo state.
234  // If there is an error or exception during one of them, only that one's
235  // side effects are rolled back, and the rest of the import list is skipped.
236  // The file may have "window" directives that cause NEW AudacityProjects
237  // to be created, and the undo states are pushed onto the latest project.
238  // If a project is created but the first file import into it fails, destroy
239  // the project.
240 
241  outTracks.clear();
242 
243  wxASSERT(mTextFile->IsOpened());
244 
245  if(mTextFile->Eof())
246  {
247  mTextFile->Close();
248  return ProgressResult::Failed;
249  }
250 
251  wxString line = mTextFile->GetFirstLine();
252 
253  while (!mTextFile->Eof())
254  {
255  lofOpenFiles(&line);
256  line = mTextFile->GetNextLine();
257  }
258 
259  // for last line
260  lofOpenFiles(&line);
261 
262  if(!mTextFile->Close())
263  return ProgressResult::Failed;
264 
265  // set any duration/offset factors for last window, as all files were called
267 
269 }
270 
272 {
273  int count = 0;
274  Track *t;
275  TrackListIterator iter(proj->GetTracks());
276 
277  t = iter.First();
278 
279  while(t) {
280  count++;
281  t = iter.Next();
282  }
283 
284  return count;
285 }
286 
294 {
295  wxStringTokenizer tok(*ln, wxT(" "));
296  wxStringTokenizer temptok1(*ln, wxT("\""));
297  wxStringTokenizer temptok2(*ln, wxT(" "));
298  int tokenplace = 0;
299 
300  wxString targetfile;
301  wxString tokenholder = tok.GetNextToken();
302 
303  if (tokenholder.IsSameAs(wxT("window"), false))
304  {
305  // set any duration/offset factors for last window, as all files were called
307 
308  if (windowCalledOnce)
309  // Cause a project to be created with the next import
310  mProject = nullptr;
311  else
312  // Apply any offset and duration directives of the first "window" line
313  // to the previously open project, not a NEW one.
314  ;
315 
316  windowCalledOnce = true;
317 
318  while (tok.HasMoreTokens())
319  {
320  tokenholder = tok.GetNextToken();
321 
322  if (tokenholder.IsSameAs(wxT("offset"), false))
323  {
324  if (tok.HasMoreTokens())
325  tokenholder = tok.GetNextToken();
326 
327  if (Internat::CompatibleToDouble(tokenholder, &scrollOffset))
328  {
329  callScrollOffset = true;
330  }
331  else
332  {
333  /* i18n-hint: You do not need to translate "LOF" */
334  AudacityMessageBox(_("Invalid window offset in LOF file."),
335  /* i18n-hint: You do not need to translate "LOF" */
336  _("LOF Error"), wxOK | wxCENTRE);
337  }
338 
339  if (tok.HasMoreTokens())
340  tokenholder = tok.GetNextToken();
341  }
342 
343  if (tokenholder.IsSameAs(wxT("duration"), false))
344  {
345  if (tok.HasMoreTokens())
346  tokenholder = tok.GetNextToken();
347 
348  if (Internat::CompatibleToDouble(tokenholder, &durationFactor))
349  {
350  callDurationFactor = true;
351  }
352  else
353  {
354  /* i18n-hint: You do not need to translate "LOF" */
355  AudacityMessageBox(_("Invalid duration in LOF file."),
356  /* i18n-hint: You do not need to translate "LOF" */
357  _("LOF Error"), wxOK | wxCENTRE);
358  }
359  } // End if statement
360 
361  if (tokenholder.IsSameAs(wxT("#")))
362  {
363  // # indicates comments; ignore line
364  tok = wxStringTokenizer(wxT(""), wxT(" "));
365  }
366  } // End while loop
367  } // End if statement handling "window" lines
368 
369  else if (tokenholder.IsSameAs(wxT("file"), false))
370  {
371 
372  // To identify filename and open it
373  tokenholder = temptok1.GetNextToken();
374  wxString targettoken = temptok1.GetNextToken();
375  targetfile = targettoken;
376 
377  // If path is relative, make absolute path from LOF path
378  if(!wxIsAbsolutePath(targetfile)) {
379  wxFileName fName(targetfile);
380  fName.Normalize(wxPATH_NORM_ALL, mLOFFileName.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR));
381  if(fName.FileExists()) {
382  targetfile = fName.GetFullPath();
383  }
384  }
385 
386  // Do recursive call to import
387 
388 #ifdef USE_MIDI
389  // If file is a midi
390  if (Importer::IsMidi(targetfile))
391  {
393  }
394 
395  // If not a midi, open audio file
396  else
397 
398 #else // !USE_MIDI
399  /* if we don't have midi support, go straight on to opening as an
400  * audio file. TODO: Some sort of message here? */
401 
402 #endif // USE_MIDI
404 
405  // Set tok to right after filename
406  temptok2.SetString(targettoken);
407  tokenplace = temptok2.CountTokens();
408 
409  for (int i = 0; i < tokenplace; i++)
410  tokenholder = tok.GetNextToken();
411 
412  if (tok.HasMoreTokens())
413  {
414  tokenholder = tok.GetNextToken();
415 
416  if (tokenholder.IsSameAs(wxT("#")))
417  {
418  // # indicates comments; ignore line
419  tok = wxStringTokenizer(wxT(""), wxT(" "));
420  }
421 
422  if (tokenholder.IsSameAs(wxT("offset"), false))
423  {
424  if (tok.HasMoreTokens())
425  tokenholder = tok.GetNextToken();
426  double offset;
427 
428  // handle an "offset" specifier
429  if (!mProject)
430  // there was an import error,
431  // presumably with its own error message
432  ;
433  else if (Internat::CompatibleToDouble(tokenholder, &offset))
434  {
435  Track *t;
437 
438  t = iter.First();
439 
440  for (int i = 1; i < CountNumTracks(mProject) - 1; i++)
441  t = iter.Next();
442 
443  // t is now the last track in the project, unless the import of
444  // all tracks failed, in which case it will be null. In that
445  // case we return because we cannot offset a non-existent track.
446  if (t == NULL) return;
447 #ifdef USE_MIDI
448  if (targetfile.AfterLast(wxT('.')).IsSameAs(wxT("mid"), false) ||
449  targetfile.AfterLast(wxT('.')).IsSameAs(wxT("midi"), false))
450  {
451  AudacityMessageBox(_("MIDI tracks cannot be offset individually, only audio files can be."),
452  _("LOF Error"), wxOK | wxCENTRE);
453  }
454  else
455 #endif
456  {
457  if (CountNumTracks(mProject) == 1)
458  t->SetOffset(offset);
459  else
460  {
461  if (t->GetLinked())
462  t->SetOffset(offset);
463 
464  t = iter.Next();
465  t->SetOffset(offset);
466  }
467  }
468 
469  // Amend the undo transaction made by import
470  mProject->TP_ModifyState(false);
471  } // end of converting "offset" argument
472  else
473  {
474  /* i18n-hint: You do not need to translate "LOF" */
475  AudacityMessageBox(_("Invalid track offset in LOF file."),
476  _("LOF Error"), wxOK | wxCENTRE);
477  }
478  } // End if statement for "offset" parameters
479  } // End if statement (more tokens after file name)
480  } // End if statement "file" lines
481 
482  else if (tokenholder.IsSameAs(wxT("#")))
483  {
484  // # indicates comments; ignore line
485  tok = wxStringTokenizer(wxT(""), wxT(" "));
486  }
487  else
488  {
489  // Couldn't parse a line
490  }
491 }
492 
494 {
495  if (!mProject)
496  return;
497 
498  bool doSomething = callDurationFactor || callScrollOffset;
499  if (callDurationFactor)
500  {
501  double longestDuration = mProject->GetTracks()->GetEndTime();
502  mProject->ZoomBy(longestDuration / durationFactor);
503  callDurationFactor = false;
504  }
505 
506  if (callScrollOffset && (scrollOffset != 0))
507  {
509  callScrollOffset = false;
510  }
511 
512  if (doSomething)
513  // Amend last undo state
514  mProject->TP_ModifyState(false);
515 }
516 
518 {
519 }
ProgressResult Import(TrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags) override
Definition: ImportLOF.cpp:226
ByteCount GetFileUncompressedBytes() override
Definition: ImportLOF.cpp:221
std::unique_ptr< ImportFileHandle > Open(const wxString &Filename) override
Definition: ImportLOF.cpp:182
ProgressResult
An ImportFileHandle for data.
Definition: ImportPlugin.h:119
const wxArrayString & GetStreamInfo() override
Definition: ImportLOF.cpp:132
wxInt32 GetStreamCount() override
Definition: ImportLOF.cpp:130
static bool IsMidi(const wxString &fName)
Definition: Import.cpp:330
unsigned long long ByteCount
Definition: ImportPlugin.h:151
double GetEndTime() const
Definition: Track.cpp:1409
AudacityProject * mProject
Definition: ImportLOF.cpp:149
void ZoomBy(double multiplier)
Definition: Project.cpp:4756
static const wxChar * exts[]
Definition: ImportLOF.cpp:98
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
#define DESC
Definition: ImportLOF.cpp:96
An ImportPlugin for LOF data.
Definition: ImportLOF.cpp:103
void lofOpenFiles(wxString *ln)
Processes a single line from a LOF text file, doing whatever is indicated on the line.
Definition: ImportLOF.cpp:293
wxString GetPluginFormatDescription()
Definition: ImportLOF.cpp:177
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:850
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:158
void doDurationAndScrollOffset()
Definition: ImportLOF.cpp:493
void TP_ScrollWindow(double scrollto) override
Definition: Project.cpp:1824
static bool CompatibleToDouble(const wxString &stringToConvert, double *result)
Convert a string to a number.
Definition: Internat.cpp:121
void TP_ModifyState(bool bWantsAutoSave) override
Definition: Project.cpp:5242
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportLOF.cpp:138
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:94
wxString GetPluginStringID()
Definition: ImportLOF.cpp:113
The interface that all file import "plugins" (if you want to call them that) must implement...
void GetLOFImportPlugin(ImportPluginList &importPluginList, UnusableImportPluginList &WXUNUSED(unusableImportPluginList))
Definition: ImportLOF.cpp:171
std::unique_ptr< wxTextFile > mTextFile
Definition: ImportLOF.cpp:146
ID3 Tags (for MP3)
Definition: Tags.h:72
An iterator for a TrackList.
Definition: Track.h:394
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:73
wxString GetFileDescription() override
Definition: ImportLOF.cpp:216
static AudacityProject * DoImportMIDI(AudacityProject *pProject, const wxString &fileName)
const wxChar * name
Definition: Distortion.cpp:94
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:300
An ImportFileHandle for LOF data.
Definition: ImportLOF.cpp:119
An UnusableImportPlugin list.
An ImportPlugin list.
TrackList * GetTracks()
Definition: Project.h:174
#define BINARY_FILE_CHECK_BUFFER_SIZE
Definition: ImportLOF.cpp:94
std::vector< std::unique_ptr< WaveTrack >> TrackHolders
Definition: ImportRaw.h:42
wxFileName mLOFFileName
Definition: ImportLOF.cpp:147
static AudacityProject * OpenProject(AudacityProject *pProject, const wxString &fileNameArg, bool addtohistory=true)
Definition: Project.cpp:2935
LOFImportFileHandle(const wxString &name, std::unique_ptr< wxTextFile > &&file)
Definition: ImportLOF.cpp:164
static int CountNumTracks(AudacityProject *proj)
Definition: ImportLOF.cpp:271