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