1 /**********************************************************************
2 
3   Audacity: A Digital Audio Editor
4 
5   Import.cpp
6 
7   Dominic Mazzoni
8 
9 *******************************************************************//**
10 
11 \file Import.cpp
12 
13   This file contains a general function which will import almost
14   any type of sampled audio file (i.e. anything except MIDI)
15   and return the tracks that were imported.  This function just
16   figures out which one to call; the actual importers are in
17   ImportPCM, ImportMP3, ImportOGG, ImportRawData, ImportLOF,
18   ImportQT, ImportFLAC and ImportAUP.
19 
20 *//***************************************************************//**
21 
22 \class Format
23 \brief Abstract base class used in importing a file.
24 
25 It's defined in Import.h
26 
27 *//***************************************************************//**
28 
29 \class Importer
30 \brief Class which actually imports the auido, using functions defined
31 in ImportPCM.cpp, ImportMP3.cpp, ImportOGG.cpp, ImportRawData.cpp,
32 ImportLOF.cpp, and ImportAUP.cpp.
33 
34 *//******************************************************************/
35 
36 
37 
38 
39 #include "Import.h"
40 
41 #include "ImportPlugin.h"
42 
43 #include <algorithm>
44 #include <unordered_set>
45 
46 #include <wx/textctrl.h>
47 #include <wx/string.h>
48 #include <wx/intl.h>
49 #include <wx/listbox.h>
50 #include <wx/log.h>
51 #include <wx/sizer.h>         //for wxBoxSizer
52 #include "../FFmpeg.h"
53 #include "FileNames.h"
54 #include "../ShuttleGui.h"
55 #include "Project.h"
56 #include "../WaveTrack.h"
57 
58 #include "Prefs.h"
59 
60 #include "../widgets/ProgressDialog.h"
61 
62 using NewChannelGroup = std::vector< std::shared_ptr<WaveTrack> >;
63 
64 // ============================================================================
65 //
66 // Return reference to singleton
67 //
68 // (Thread-safe...no active threading during construction or after destruction)
69 // ============================================================================
70 Importer Importer::mInstance;
Get()71 Importer & Importer::Get()
72 {
73    return mInstance;
74 }
75 
Importer()76 Importer::Importer()
77 {
78 }
79 
~Importer()80 Importer::~Importer()
81 {
82 }
83 
sImportPluginList()84 ImportPluginList &Importer::sImportPluginList()
85 {
86    static ImportPluginList theList;
87    return theList;
88 }
89 
90 namespace {
91 static const auto PathStart = wxT("Importers");
92 
sRegistry()93 static Registry::GroupItem &sRegistry()
94 {
95    static Registry::TransparentGroupItem<> registry{ PathStart };
96    return registry;
97 }
98 
99 struct ImporterItem final : Registry::SingleItem {
ImporterItem__anon04459edf0111::ImporterItem100    ImporterItem( const Identifier &id, std::unique_ptr<ImportPlugin> pPlugin )
101       : SingleItem{ id }
102       , mpPlugin{ std::move( pPlugin ) }
103    {}
104 
105    std::unique_ptr<ImportPlugin> mpPlugin;
106 };
107 }
108 
RegisteredImportPlugin(const Identifier & id,std::unique_ptr<ImportPlugin> pPlugin,const Registry::Placement & placement)109 Importer::RegisteredImportPlugin::RegisteredImportPlugin(
110    const Identifier &id,
111    std::unique_ptr<ImportPlugin> pPlugin,
112    const Registry::Placement &placement )
113 {
114    if ( pPlugin )
115       Registry::RegisterItem( sRegistry(), placement,
116          std::make_unique< ImporterItem >( id, std::move( pPlugin ) ) );
117 }
118 
sUnusableImportPluginList()119 UnusableImportPluginList &Importer::sUnusableImportPluginList()
120 {
121    static UnusableImportPluginList theList;
122    return theList;
123 }
124 
RegisteredUnusableImportPlugin(std::unique_ptr<UnusableImportPlugin> pPlugin)125 Importer::RegisteredUnusableImportPlugin::RegisteredUnusableImportPlugin(
126    std::unique_ptr<UnusableImportPlugin> pPlugin )
127 {
128    if ( pPlugin )
129       sUnusableImportPluginList().emplace_back( std::move( pPlugin ) );
130 }
131 
Initialize()132 bool Importer::Initialize()
133 {
134    // build the list of import plugin and/or unusableImporters.
135    // order is significant.  If none match, they will all be tried
136    // in the order defined here.
137 
138    using namespace Registry;
139    static OrderingPreferenceInitializer init{
140       PathStart,
141       { {wxT(""), wxT("AUP,PCM,OGG,FLAC,MP3,LOF,FFmpeg") } }
142       // QT and GStreamer are only conditionally compiled and would get
143       // placed at the end if present
144    };
145 
146    static struct MyVisitor final : Visitor {
147       MyVisitor()
148       {
149          // Once only, visit the registry to collect the plug-ins properly
150          // sorted
151          TransparentGroupItem<> top{ PathStart };
152          Registry::Visit( *this, &top, &sRegistry() );
153       }
154 
155       void Visit( SingleItem &item, const Path &path ) override
156       {
157          sImportPluginList().push_back(
158             static_cast<ImporterItem&>( item ).mpPlugin.get() );
159       }
160    } visitor;
161 
162    // Ordering of the unusable plugin list is not important.
163 
164    ExtImportItems{}.swap(mExtImportItems);
165 
166    ReadImportItems();
167 
168    return true;
169 }
170 
Terminate()171 bool Importer::Terminate()
172 {
173    WriteImportItems();
174 
175    return true;
176 }
177 
178 FileNames::FileTypes
GetFileTypes(const FileNames::FileType & extraType)179 Importer::GetFileTypes( const FileNames::FileType &extraType )
180 {
181    // Construct the filter
182    FileNames::FileTypes fileTypes{
183       FileNames::AllFiles,
184       // Will fill in the list of extensions later:
185       { XO("All supported files"), {} },
186       FileNames::AudacityProjects
187    };
188 
189    if ( !extraType.extensions.empty() )
190       fileTypes.push_back( extraType );
191 
192    FileNames::FileTypes l;
193    for(const auto &importPlugin : sImportPluginList())
194    {
195       l.emplace_back(importPlugin->GetPluginFormatDescription(),
196                                importPlugin->GetSupportedExtensions());
197    }
198 
199    FileExtensions extraExtensions = FileNames::AudacityProjects.extensions;
200    extraExtensions.insert(extraExtensions.end(),
201                           extraType.extensions.begin(),
202                           extraType.extensions.end());
203 
204    using ExtensionSet = std::unordered_set< FileExtension >;
205    FileExtensions allList = FileNames::AudacityProjects.extensions, newList;
206    allList.insert(allList.end(), extraType.extensions.begin(), extraType.extensions.end());
207    ExtensionSet allSet{ allList.begin(), allList.end() }, newSet;
208    for ( const auto &format : l ) {
209       newList.clear();
210       newSet.clear();
211       for ( const auto &extension : format.extensions ) {
212          if ( newSet.insert( extension ).second )
213             newList.push_back( extension );
214          if ( allSet.insert( extension ).second )
215             allList.push_back( extension );
216       }
217       fileTypes.push_back( { format.description, newList } );
218    }
219 
220    fileTypes[1].extensions = allList;
221    return fileTypes;
222 }
223 
SetLastOpenType(const FileNames::FileType & type)224 void Importer::SetLastOpenType( const FileNames::FileType &type )
225 {
226    // PRL:  Preference key /LastOpenType, unusually, stores a localized
227    // string!
228    // The bad consequences of a change of locale are not severe -- only that
229    // a default choice of file type for an open dialog is not remembered
230    gPrefs->Write(wxT("/LastOpenType"), type.description.Translation());
231    gPrefs->Flush();
232 }
233 
SetDefaultOpenType(const FileNames::FileType & type)234 void Importer::SetDefaultOpenType( const FileNames::FileType &type )
235 {
236    // PRL:  Preference key /DefaultOpenType, unusually, stores a localized
237    // string!
238    // The bad consequences of a change of locale are not severe -- only that
239    // a default choice of file type for an open dialog is not remembered
240    gPrefs->Write(wxT("/DefaultOpenType"), type.description.Translation());
241    gPrefs->Flush();
242 }
243 
SelectDefaultOpenType(const FileNames::FileTypes & fileTypes)244 size_t Importer::SelectDefaultOpenType( const FileNames::FileTypes &fileTypes )
245 {
246    wxString defaultValue;
247    if ( !fileTypes.empty() )
248       defaultValue = fileTypes[0].description.Translation();
249 
250    wxString type = gPrefs->Read(wxT("/DefaultOpenType"), defaultValue);
251    // Convert the type to the filter index
252    auto begin = fileTypes.begin();
253    auto index = std::distance(
254       begin,
255       std::find_if( begin, fileTypes.end(),
256          [&type](const FileNames::FileType &fileType){
257             return fileType.description.Translation() == type; } ) );
258    return (index == fileTypes.size()) ? 0 : index;
259 }
260 
StringToList(wxString & str,wxString & delims,wxArrayString & list,wxStringTokenizerMode mod)261 void Importer::StringToList(wxString &str, wxString &delims, wxArrayString &list, wxStringTokenizerMode mod)
262 {
263    wxStringTokenizer toker;
264 
265    for (toker.SetString(str, delims, mod);
266       toker.HasMoreTokens(); list.push_back(toker.GetNextToken()));
267 }
268 
ReadImportItems()269 void Importer::ReadImportItems()
270 {
271    int item_counter = 0;
272    wxStringTokenizer toker;
273    wxString item_name;
274    wxString item_value;
275 
276    ExtImportItems{}.swap(mExtImportItems);
277    /* Rule string format is:
278     * extension1:extension2:extension3\mime_type1:mime_type2:mime_type3|filter1:filter2:filter3\unusedfilter1:unusedfilter2
279     * backslashes are escaped and unescaped internally
280     */
281    for (item_counter = 0; true; item_counter++)
282    {
283       wxString condition, filters, used_filters, unused_filters, extensions, mime_types;
284       item_name.Printf (wxT("/ExtImportItems/Item%d"), item_counter);
285       /* Break at first non-existent item */
286       if (!gPrefs->Read(item_name, &item_value))
287         break;
288 
289       toker.SetString(item_value, wxT("|"), wxTOKEN_RET_EMPTY_ALL);
290       /* Break at first broken item */
291       if (toker.CountTokens() != 2)
292         break;
293 
294       auto new_item = std::make_unique<ExtImportItem>();
295 
296       /* First token is the filtering condition, second - the filter list */
297       condition = toker.GetNextToken();
298       filters = toker.GetNextToken();
299 
300       /* Condition token consists of extension list and mime type list
301        * mime type list can be omitted entirely (complete with '\' separator)*/
302       toker.SetString(condition, wxT("\\"), wxTOKEN_RET_EMPTY_ALL);
303       extensions = toker.GetNextToken();
304       if (toker.HasMoreTokens())
305         mime_types = toker.GetNextToken();
306 
307       wxString delims(wxT(":"));
308       StringToList (extensions, delims, new_item->extensions);
309 
310       if (!mime_types.empty())
311          StringToList (mime_types, delims, new_item->mime_types);
312 
313       /* Filter token consists of used and unused filter lists */
314       toker.SetString(filters, wxT("\\"), wxTOKEN_RET_EMPTY_ALL);
315       used_filters = toker.GetNextToken();
316       if (toker.HasMoreTokens())
317         unused_filters = toker.GetNextToken();
318 
319       StringToList (used_filters, delims, new_item->filters);
320 
321       if (!unused_filters.empty())
322       {
323          /* Filters are stored in one list, but the position at which
324           * unused filters start is remembered
325           */
326          new_item->divider = new_item->filters.size();
327          StringToList (unused_filters, delims, new_item->filters);
328       }
329       else
330          new_item->divider = -1;
331 
332       /* Find corresponding filter object for each filter ID */
333       for (size_t i = 0; i < new_item->filters.size(); i++)
334       {
335          bool found = false;
336          for (const auto &importPlugin : sImportPluginList())
337          {
338             if (importPlugin->GetPluginStringID() == new_item->filters[i])
339             {
340                new_item->filter_objects.push_back(importPlugin);
341                found = true;
342                break;
343             }
344          }
345          /* IDs that do not have corresponding filters, will be shown as-is */
346          if (!found)
347            new_item->filter_objects.push_back(nullptr);
348       }
349       /* Find all filter objects that are not present in the filter list */
350       for (const auto &importPlugin : sImportPluginList())
351       {
352          bool found = false;
353          for (size_t i = 0; i < new_item->filter_objects.size(); i++)
354          {
355             if (importPlugin == new_item->filter_objects[i])
356             {
357                found = true;
358                break;
359             }
360          }
361          /* Add these filters at the bottom of used filter list */
362          if (!found)
363          {
364             int index = new_item->divider;
365             if (new_item->divider < 0)
366                index = new_item->filters.size();
367             new_item->filters.insert(
368                new_item->filters.begin() + index,
369                importPlugin->GetPluginStringID());
370             new_item->filter_objects.insert(
371                new_item->filter_objects.begin() + index, importPlugin);
372             if (new_item->divider >= 0)
373                new_item->divider++;
374          }
375       }
376       this->mExtImportItems.push_back( std::move(new_item) );
377    }
378 }
379 
WriteImportItems()380 void Importer::WriteImportItems()
381 {
382    size_t i;
383    wxString val, name;
384    for (i = 0; i < this->mExtImportItems.size(); i++)
385    {
386       ExtImportItem *item = mExtImportItems[i].get();
387       val.clear();
388 
389       for (size_t j = 0; j < item->extensions.size(); j++)
390       {
391          val.Append (item->extensions[j]);
392          if (j < item->extensions.size() - 1)
393             val.Append (wxT(":"));
394       }
395       val.Append (wxT("\\"));
396       for (size_t j = 0; j < item->mime_types.size(); j++)
397       {
398          val.Append (item->mime_types[j]);
399          if (j < item->mime_types.size() - 1)
400             val.Append (wxT(":"));
401       }
402       val.Append (wxT("|"));
403       for (size_t j = 0; j < item->filters.size() && ((int) j < item->divider || item->divider < 0); j++)
404       {
405          val.Append (item->filters[j]);
406          if (j < item->filters.size() - 1 && ((int) j < item->divider - 1 || item->divider < 0))
407             val.Append (wxT(":"));
408       }
409       if (item->divider >= 0)
410       {
411          val.Append (wxT("\\"));
412          for (size_t j = item->divider; j < item->filters.size(); j++)
413          {
414             val.Append (item->filters[j]);
415             if (j < item->filters.size() - 1)
416                val.Append (wxT(":"));
417          }
418       }
419       name.Printf (wxT("/ExtImportItems/Item%d"), (int)i);
420       gPrefs->Write (name, val);
421       gPrefs->Flush();
422    }
423    /* If we used to have more items than we have now, DELETE the excess items.
424    We just keep deleting items and incrementing until we find there aren't any
425    more to DELETE.*/
426    i = this->mExtImportItems.size();
427    do {
428      name.Printf (wxT("/ExtImportItems/Item%d"), (int)i);
429      // No item to DELETE?  Then it's time to finish.
430      if (!gPrefs->Read(name, &val))
431         break;
432      // Failure to DELETE probably means a read-only config file.
433      // no point continuing.
434      // TODO: Possibly report (once).
435      if( !gPrefs->DeleteEntry (name, false))
436         break;
437      i++;
438    } while( true );
439 }
440 
CreateDefaultImportItem()441 std::unique_ptr<ExtImportItem> Importer::CreateDefaultImportItem()
442 {
443    auto new_item = std::make_unique<ExtImportItem>();
444    new_item->extensions.push_back(wxT("*"));
445    new_item->mime_types.push_back(wxT("*"));
446 
447    for (const auto &importPlugin : sImportPluginList())
448    {
449       new_item->filters.push_back(importPlugin->GetPluginStringID());
450       new_item->filter_objects.push_back(importPlugin);
451    }
452    new_item->divider = -1;
453    return new_item;
454 }
455 
456 // returns number of tracks imported
Import(AudacityProject & project,const FilePath & fName,WaveTrackFactory * trackFactory,TrackHolders & tracks,Tags * tags,TranslatableString & errorMessage)457 bool Importer::Import( AudacityProject &project,
458                      const FilePath &fName,
459                      WaveTrackFactory *trackFactory,
460                      TrackHolders &tracks,
461                      Tags *tags,
462                      TranslatableString &errorMessage)
463 {
464    AudacityProject *pProj = &project;
465    auto cleanup = valueRestorer( pProj->mbBusyImporting, true );
466 
467    const FileExtension extension{ fName.AfterLast(wxT('.')) };
468 
469    // Always refuse to import MIDI, even though the FFmpeg plugin pretends to know how (but makes very bad renderings)
470 #ifdef USE_MIDI
471    // MIDI files must be imported, not opened
472    if (FileNames::IsMidi(fName)) {
473       errorMessage = XO(
474 "\"%s\" \nis a MIDI file, not an audio file. \nAudacity cannot open this type of file for playing, but you can\nedit it by clicking File > Import > MIDI.")
475          .Format( fName );
476       return false;
477    }
478 #endif
479 
480    // Bug #2647: Peter has a Word 2000 .doc file that is recognized and imported by FFmpeg.
481    if (wxFileName(fName).GetExt() == wxT("doc")) {
482       errorMessage =
483          XO("\"%s\" \nis a not an audio file. \nAudacity cannot open this type of file.")
484          .Format( fName );
485       return false;
486    }
487 
488    using ImportPluginPtrs = std::vector< ImportPlugin* >;
489 
490    // This list is used to call plugins in correct order
491    ImportPluginPtrs importPlugins;
492 
493    // This list is used to remember plugins that should have been compatible with the file.
494    ImportPluginPtrs compatiblePlugins;
495 
496    // Not implemented (yet?)
497    wxString mime_type = wxT("*");
498 
499    // First, add user-selected filter
500    bool usersSelectionOverrides;
501    gPrefs->Read(wxT("/ExtendedImport/OverrideExtendedImportByOpenFileDialogChoice"), &usersSelectionOverrides, false);
502 
503    if (usersSelectionOverrides)
504    {
505       // If user explicitly selected a filter,
506       // then we should try importing via corresponding plugin first
507       wxString type = gPrefs->Read(wxT("/LastOpenType"),wxT(""));
508 
509       wxLogDebug(wxT("LastOpenType is %s"),type);
510       wxLogDebug(wxT("OverrideExtendedImportByOpenFileDialogChoice is %i"),usersSelectionOverrides);
511 
512       for (const auto &plugin : sImportPluginList())
513       {
514          if (plugin->GetPluginFormatDescription().Translation() == type )
515          {
516             // This plugin corresponds to user-selected filter, try it first.
517             wxLogDebug(wxT("Inserting %s"),plugin->GetPluginStringID());
518             importPlugins.insert(importPlugins.begin(), plugin);
519          }
520       }
521    }
522 
523    wxLogMessage(wxT("File name is %s"), fName);
524    wxLogMessage(wxT("Mime type is %s"), mime_type.Lower());
525 
526    for (const auto &uItem : mExtImportItems)
527    {
528       ExtImportItem *item = uItem.get();
529       bool matches_ext = false, matches_mime = false;
530       wxLogDebug(wxT("Testing extensions"));
531       for (size_t j = 0; j < item->extensions.size(); j++)
532       {
533          wxLogDebug(wxT("%s"), item->extensions[j].Lower());
534          if (wxMatchWild (item->extensions[j].Lower(),fName.Lower(), false))
535          {
536             wxLogDebug(wxT("Match!"));
537             matches_ext = true;
538             break;
539          }
540       }
541       if (item->extensions.size() == 0)
542       {
543          wxLogDebug(wxT("Match! (empty list)"));
544          matches_ext = true;
545       }
546       if (matches_ext)
547          wxLogDebug(wxT("Testing mime types"));
548       else
549          wxLogDebug(wxT("Not testing mime types"));
550       for (size_t j = 0; matches_ext && j < item->mime_types.size(); j++)
551       {
552          if (wxMatchWild (item->mime_types[j].Lower(),mime_type.Lower(), false))
553          {
554             wxLogDebug(wxT("Match!"));
555             matches_mime = true;
556             break;
557          }
558       }
559       if (item->mime_types.size() == 0)
560       {
561          wxLogDebug(wxT("Match! (empty list)"));
562          matches_mime = true;
563       }
564       if (matches_ext && matches_mime)
565       {
566          wxLogDebug(wxT("Complete match!"));
567          for (size_t j = 0; j < item->filter_objects.size() && (item->divider < 0 || (int) j < item->divider); j++)
568          {
569             // the filter_object can be NULL if a suitable importer was not found
570             // this happens when we recompile with --without-ffmpeg and there
571             // is still ffmpeg in prefs from previous --with-ffmpeg builds
572             if (!(item->filter_objects[j]))
573                continue;
574             wxLogDebug(wxT("Inserting %s"),item->filter_objects[j]->GetPluginStringID());
575             importPlugins.push_back(item->filter_objects[j]);
576          }
577       }
578    }
579 
580    // Add all plugins that support the extension
581    for (const auto &plugin : sImportPluginList())
582    {
583       // Make sure its not already in the list
584       if (importPlugins.end() ==
585           std::find(importPlugins.begin(), importPlugins.end(), plugin))
586       {
587          if (plugin->SupportsExtension(extension))
588          {
589             wxLogDebug(wxT("Appending %s"),plugin->GetPluginStringID());
590             importPlugins.push_back(plugin);
591          }
592       }
593    }
594 
595    // Add remaining plugins
596    for (const auto &plugin : sImportPluginList())
597    {
598       // Make sure its not already in the list
599       if (importPlugins.end() ==
600             std::find(importPlugins.begin(), importPlugins.end(), plugin))
601       {
602          wxLogDebug(wxT("Appending %s"),plugin->GetPluginStringID());
603          importPlugins.push_back(plugin);
604       }
605    }
606 
607    // Try the import plugins, in the permuted sequences just determined
608    for (const auto plugin : importPlugins)
609    {
610       // Try to open the file with this plugin (probe it)
611       wxLogMessage(wxT("Opening with %s"),plugin->GetPluginStringID());
612       auto inFile = plugin->Open(fName, pProj);
613       if ( (inFile != NULL) && (inFile->GetStreamCount() > 0) )
614       {
615          wxLogMessage(wxT("Open(%s) succeeded"), fName);
616          // File has more than one stream - display stream selector
617          if (inFile->GetStreamCount() > 1)
618          {
619             ImportStreamDialog ImportDlg(inFile.get(), NULL, -1, XO("Select stream(s) to import"));
620 
621             if (ImportDlg.ShowModal() == wxID_CANCEL)
622             {
623                return false;
624             }
625          }
626          // One stream - import it by default
627          else
628             inFile->SetStreamUsage(0,TRUE);
629 
630          auto res = inFile->Import(trackFactory, tracks, tags);
631 
632          if (res == ProgressResult::Success || res == ProgressResult::Stopped)
633          {
634             // LOF ("list-of-files") has different semantics
635             if (extension.IsSameAs(wxT("lof"), false))
636             {
637                return true;
638             }
639 
640             // AUP ("legacy projects") have different semantics
641             if (extension.IsSameAs(wxT("aup"), false))
642             {
643                return true;
644             }
645 
646             auto end = tracks.end();
647             auto iter = std::remove_if( tracks.begin(), end,
648                std::mem_fn( &NewChannelGroup::empty ) );
649             if ( iter != end ) {
650                // importer shouldn't give us empty groups of channels!
651                wxASSERT(false);
652                // But correct that and proceed anyway
653                tracks.erase( iter, end );
654             }
655             if (tracks.size() > 0)
656             {
657                // success!
658                return true;
659             }
660          }
661 
662          if (res == ProgressResult::Cancelled || res == ProgressResult::Failed)
663          {
664             return false;
665          }
666 
667          // We could exit here since we had a match on the file extension,
668          // but there may be another plug-in that can import the file and
669          // that may recognize the extension, so we allow the loop to
670          // continue.
671       }
672    }
673    wxLogError(wxT("Importer::Import: Opening failed."));
674 
675    // None of our plugins can handle this file.  It might be that
676    // Audacity supports this format, but support was not compiled in.
677    // If so, notify the user of this fact
678    for (const auto &unusableImportPlugin : sUnusableImportPluginList())
679    {
680       if( unusableImportPlugin->SupportsExtension(extension) )
681       {
682          errorMessage = XO("This version of Audacity was not compiled with %s support.")
683             .Format( unusableImportPlugin->GetPluginFormatDescription() );
684          return false;
685       }
686    }
687 
688    /* warnings for unsupported data types */
689 
690    if (compatiblePlugins.empty())
691    {
692       // if someone has sent us a .cda file, send them away
693       if (extension.IsSameAs(wxT("cda"), false)) {
694          errorMessage = XO(
695 /* i18n-hint: %s will be the filename */
696 "\"%s\" is an audio CD track. \nAudacity cannot open audio CDs directly. \nExtract (rip) the CD tracks to an audio format that \nAudacity can import, such as WAV or AIFF.")
697             .Format( fName );
698          return false;
699       }
700 
701       // playlist type files
702       if ((extension.IsSameAs(wxT("m3u"), false))||(extension.IsSameAs(wxT("ram"), false))||(extension.IsSameAs(wxT("pls"), false))) {
703          errorMessage = XO(
704 /* i18n-hint: %s will be the filename */
705 "\"%s\" is a playlist file. \nAudacity cannot open this file because it only contains links to other files. \nYou may be able to open it in a text editor and download the actual audio files.")
706             .Format( fName );
707          return false;
708       }
709       //WMA files of various forms
710       if ((extension.IsSameAs(wxT("wma"), false))||(extension.IsSameAs(wxT("asf"), false))) {
711          errorMessage = XO(
712 /* i18n-hint: %s will be the filename */
713 "\"%s\" is a Windows Media Audio file. \nAudacity cannot open this type of file due to patent restrictions. \nYou need to convert it to a supported audio format, such as WAV or AIFF.")
714             .Format( fName );
715          return false;
716       }
717       //AAC files of various forms (probably not encrypted)
718       if ((extension.IsSameAs(wxT("aac"), false))||(extension.IsSameAs(wxT("m4a"), false))||(extension.IsSameAs(wxT("m4r"), false))||(extension.IsSameAs(wxT("mp4"), false))) {
719          errorMessage = XO(
720 /* i18n-hint: %s will be the filename */
721 "\"%s\" is an Advanced Audio Coding file.\nWithout the optional FFmpeg library, Audacity cannot open this type of file.\nOtherwise, you need to convert it to a supported audio format, such as WAV or AIFF.")
722             .Format( fName );
723          return false;
724       }
725       // encrypted itunes files
726       if ((extension.IsSameAs(wxT("m4p"), false))) {
727          errorMessage = XO(
728 /* i18n-hint: %s will be the filename */
729 "\"%s\" is an encrypted audio file. \nThese typically are from an online music store. \nAudacity cannot open this type of file due to the encryption. \nTry recording the file into Audacity, or burn it to audio CD then \nextract the CD track to a supported audio format such as WAV or AIFF.")
730             .Format( fName );
731          return false;
732       }
733       // Real Inc. files of various sorts
734       if ((extension.IsSameAs(wxT("ra"), false))||(extension.IsSameAs(wxT("rm"), false))||(extension.IsSameAs(wxT("rpm"), false))) {
735          errorMessage = XO(
736 /* i18n-hint: %s will be the filename */
737 "\"%s\" is a RealPlayer media file. \nAudacity cannot open this proprietary format. \nYou need to convert it to a supported audio format, such as WAV or AIFF.")
738             .Format( fName );
739          return false;
740       }
741 
742       // Other notes-based formats
743       if ((extension.IsSameAs(wxT("kar"), false))||(extension.IsSameAs(wxT("mod"), false))||(extension.IsSameAs(wxT("rmi"), false))) {
744          errorMessage = XO(
745 /* i18n-hint: %s will be the filename */
746 "\"%s\" is a notes-based file, not an audio file. \nAudacity cannot open this type of file. \nTry converting it to an audio file such as WAV or AIFF and \nthen import it, or record it into Audacity.")
747             .Format( fName );
748          return false;
749       }
750 
751       // MusePack files
752       if ((extension.IsSameAs(wxT("mp+"), false))||(extension.IsSameAs(wxT("mpc"), false))||(extension.IsSameAs(wxT("mpp"), false))) {
753          errorMessage = XO(
754 /* i18n-hint: %s will be the filename */
755 "\"%s\" is a Musepack audio file. \nAudacity cannot open this type of file. \nIf you think it might be an mp3 file, rename it to end with \".mp3\" \nand try importing it again. Otherwise you need to convert it to a supported audio \nformat, such as WAV or AIFF.")
756             .Format( fName );
757          return false;
758       }
759 
760       // WavPack files
761       if ((extension.IsSameAs(wxT("wv"), false))||(extension.IsSameAs(wxT("wvc"), false))) {
762          errorMessage = XO(
763 /* i18n-hint: %s will be the filename */
764 "\"%s\" is a Wavpack audio file. \nAudacity cannot open this type of file. \nYou need to convert it to a supported audio format, such as WAV or AIFF.")
765             .Format( fName );
766          return false;
767       }
768 
769       // AC3 files
770       if ((extension.IsSameAs(wxT("ac3"), false))) {
771          errorMessage = XO(
772 /* i18n-hint: %s will be the filename */
773 "\"%s\" is a Dolby Digital audio file. \nAudacity cannot currently open this type of file. \nYou need to convert it to a supported audio format, such as WAV or AIFF.")
774             .Format( fName );
775          return false;
776       }
777 
778       // Speex files
779       if ((extension.IsSameAs(wxT("spx"), false))) {
780          errorMessage = XO(
781 /* i18n-hint: %s will be the filename */
782 "\"%s\" is an Ogg Speex audio file. \nAudacity cannot currently open this type of file. \nYou need to convert it to a supported audio format, such as WAV or AIFF.")
783             .Format( fName );
784          return false;
785       }
786 
787       // Video files of various forms
788       if ((extension.IsSameAs(wxT("mpg"), false))||(extension.IsSameAs(wxT("mpeg"), false))||(extension.IsSameAs(wxT("avi"), false))||(extension.IsSameAs(wxT("wmv"), false))||(extension.IsSameAs(wxT("rv"), false))) {
789          errorMessage = XO(
790 /* i18n-hint: %s will be the filename */
791 "\"%s\" is a video file. \nAudacity cannot currently open this type of file. \nYou need to extract the audio to a supported format, such as WAV or AIFF.")
792             .Format( fName );
793          return false;
794       }
795 
796       if( !wxFileExists(fName)){
797          errorMessage = XO( "File \"%s\" not found.").Format( fName );
798          return false;
799       }
800 
801       // we were not able to recognize the file type
802       errorMessage = XO(
803 /* i18n-hint: %s will be the filename */
804 "Audacity did not recognize the type of the file '%s'.\n\n%sFor uncompressed files, also try File > Import > Raw Data.")
805          .Format( fName,
806 #if defined(USE_FFMPEG)
807                !FFmpegFunctions::Load()
808                   ? XO("Try installing FFmpeg.\n\n") :
809 #endif
810                   Verbatim("") );
811    }
812    else
813    {
814       // We DO have a plugin for this file, but import failed.
815       TranslatableString pluglist;
816 
817       for (const auto &plugin : compatiblePlugins)
818       {
819          if (pluglist.empty())
820            pluglist = plugin->GetPluginFormatDescription();
821          else
822            pluglist = XO("%s, %s")
823                .Format( pluglist, plugin->GetPluginFormatDescription() );
824       }
825 
826       errorMessage = XO(
827 /* i18n-hint: %s will be the filename */
828 "Audacity recognized the type of the file '%s'.\nImporters supposedly supporting such files are:\n%s,\nbut none of them understood this file format.")
829          .Format( fName, pluglist );
830    }
831 
832    return false;
833 }
834 
835 //-------------------------------------------------------------------------
836 // ImportStreamDialog
837 //-------------------------------------------------------------------------
838 
BEGIN_EVENT_TABLE(ImportStreamDialog,wxDialogWrapper)839 BEGIN_EVENT_TABLE( ImportStreamDialog, wxDialogWrapper )
840    EVT_BUTTON( wxID_OK, ImportStreamDialog::OnOk )
841    EVT_BUTTON( wxID_CANCEL, ImportStreamDialog::OnCancel )
842 END_EVENT_TABLE()
843 
844 ImportStreamDialog::ImportStreamDialog( ImportFileHandle *_mFile, wxWindow *parent, wxWindowID id, const TranslatableString &title,
845                                        const wxPoint &position, const wxSize& size, long style ):
846 wxDialogWrapper( parent, id, title, position, size, style | wxRESIZE_BORDER )
847 {
848    SetName();
849 
850    mFile = _mFile;
851    scount = mFile->GetStreamCount();
852    for (wxInt32 i = 0; i < scount; i++)
853       mFile->SetStreamUsage(i, FALSE);
854 
855    ShuttleGui S{ this, eIsCreating };
856    {
857       S.SetBorder( 5 );
858 
859       StreamList =
860       S
861          .Prop(1)
862          .Position(wxEXPAND | wxALIGN_LEFT | wxALL)
863          .Style(wxLB_EXTENDED | wxLB_ALWAYS_SB)
864          .AddListBox(
865             transform_container<wxArrayStringEx>(
866                mFile->GetStreamInfo(),
867                std::mem_fn( &TranslatableString::Translation ) ) );
868 
869       S.AddStandardButtons();
870    }
871 
872    SetAutoLayout(true);
873    GetSizer()->Fit( this );
874 
875    SetSize( 400, 200 );
876 }
877 
~ImportStreamDialog()878 ImportStreamDialog::~ImportStreamDialog()
879 {
880 
881 }
882 
OnOk(wxCommandEvent & WXUNUSED (event))883 void ImportStreamDialog::OnOk(wxCommandEvent & WXUNUSED(event))
884 {
885    wxArrayInt selitems;
886    int sels = StreamList->GetSelections(selitems);
887    for (wxInt32 i = 0; i < sels; i++)
888       mFile->SetStreamUsage(selitems[i],TRUE);
889    EndModal( wxID_OK );
890 }
891 
OnCancel(wxCommandEvent & WXUNUSED (event))892 void ImportStreamDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
893 {
894    EndModal( wxID_CANCEL );
895 }
896 
897 BoolSetting NewImportingSession{ L"/NewImportingSession", false };
898