You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1717 lines
45 KiB

  1. /*!********************************************************************
  2. Audacity: A Digital Audio Editor
  3. @file ImportAUP.cpp
  4. @brief Upgrading project file formats from before version 3
  5. *//****************************************************************//**
  6. \class AUPImportFileHandle
  7. \brief An ImportFileHandle for AUP files (pre-AUP3)
  8. *//****************************************************************//**
  9. \class AUPImportPlugin
  10. \brief An ImportPlugin for AUP files (pre-AUP3)
  11. *//*******************************************************************/
  12. #include "Import.h"
  13. #include "ImportPlugin.h"
  14. #include "../Envelope.h"
  15. #include "../FileFormats.h"
  16. #include "../FileNames.h"
  17. #include "../LabelTrack.h"
  18. #if defined(USE_MIDI)
  19. #include "../NoteTrack.h"
  20. #endif
  21. #include "../Prefs.h"
  22. #include "../Project.h"
  23. #include "../ProjectFileIO.h"
  24. #include "../ProjectFileManager.h"
  25. #include "../ProjectHistory.h"
  26. #include "../ProjectSelectionManager.h"
  27. #include "../ProjectSettings.h"
  28. #include "../Sequence.h"
  29. #include "../Tags.h"
  30. #include "../TimeTrack.h"
  31. #include "../ViewInfo.h"
  32. #include "../WaveClip.h"
  33. #include "../WaveTrack.h"
  34. #include "../toolbars/SelectionBar.h"
  35. #include "../widgets/AudacityMessageBox.h"
  36. #include "../widgets/NumericTextCtrl.h"
  37. #include "../widgets/ProgressDialog.h"
  38. #include "../xml/XMLFileReader.h"
  39. #include "../wxFileNameWrapper.h"
  40. #include <map>
  41. #define DESC XO("AUP project files (*.aup)")
  42. static const auto exts = {wxT("aup")};
  43. #include <wx/dir.h>
  44. #include <wx/ffile.h>
  45. #include <wx/file.h>
  46. #include <wx/frame.h>
  47. #include <wx/string.h>
  48. #include <wx/utils.h>
  49. class AUPImportFileHandle;
  50. using ImportHandle = std::unique_ptr<ImportFileHandle>;
  51. using NewChannelGroup = std::vector<std::shared_ptr<WaveTrack>>;
  52. class AUPImportPlugin final : public ImportPlugin
  53. {
  54. public:
  55. AUPImportPlugin();
  56. ~AUPImportPlugin();
  57. wxString GetPluginStringID() override;
  58. TranslatableString GetPluginFormatDescription() override;
  59. ImportHandle Open(const FilePath &fileName,
  60. AudacityProject *project) override;
  61. };
  62. class AUPImportFileHandle final : public ImportFileHandle,
  63. public XMLTagHandler
  64. {
  65. public:
  66. AUPImportFileHandle(const FilePath &name,
  67. AudacityProject *project);
  68. ~AUPImportFileHandle();
  69. TranslatableString GetFileDescription() override;
  70. ByteCount GetFileUncompressedBytes() override;
  71. ProgressResult Import(WaveTrackFactory *trackFactory,
  72. TrackHolders &outTracks,
  73. Tags *tags) override;
  74. wxInt32 GetStreamCount() override;
  75. const TranslatableStrings &GetStreamInfo() override;
  76. void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override;
  77. bool Open();
  78. private:
  79. struct node
  80. {
  81. wxString parent;
  82. wxString tag;
  83. XMLTagHandler *handler;
  84. };
  85. using stack = std::vector<struct node>;
  86. bool HandleXMLTag(const wxChar *tag, const wxChar **attrs) override;
  87. void HandleXMLEndTag(const wxChar *tag) override;
  88. XMLTagHandler *HandleXMLChild(const wxChar *tag) override;
  89. bool HandleProject(XMLTagHandler *&handle);
  90. bool HandleLabelTrack(XMLTagHandler *&handle);
  91. bool HandleNoteTrack(XMLTagHandler *&handle);
  92. bool HandleTimeTrack(XMLTagHandler *&handle);
  93. bool HandleWaveTrack(XMLTagHandler *&handle);
  94. bool HandleTags(XMLTagHandler *&handle);
  95. bool HandleTag(XMLTagHandler *&handle);
  96. bool HandleLabel(XMLTagHandler *&handle);
  97. bool HandleWaveClip(XMLTagHandler *&handle);
  98. bool HandleSequence(XMLTagHandler *&handle);
  99. bool HandleWaveBlock(XMLTagHandler *&handle);
  100. bool HandleEnvelope(XMLTagHandler *&handle);
  101. bool HandleControlPoint(XMLTagHandler *&handle);
  102. bool HandleSimpleBlockFile(XMLTagHandler *&handle);
  103. bool HandleSilentBlockFile(XMLTagHandler *&handle);
  104. bool HandlePCMAliasBlockFile(XMLTagHandler *&handle);
  105. bool HandleImport(XMLTagHandler *&handle);
  106. // Called in first pass to collect information about blocks
  107. void AddFile(sampleCount len,
  108. sampleFormat format,
  109. const FilePath &blockFilename = wxEmptyString,
  110. const FilePath &audioFilename = wxEmptyString,
  111. sampleCount origin = 0,
  112. int channel = 0);
  113. // These two use the collected file information in a second pass
  114. bool AddSilence(sampleCount len);
  115. bool AddSamples(const FilePath &blockFilename,
  116. const FilePath &audioFilename,
  117. sampleCount len,
  118. sampleFormat format,
  119. sampleCount origin = 0,
  120. int channel = 0);
  121. bool SetError(const TranslatableString &msg);
  122. bool SetWarning(const TranslatableString &msg);
  123. private:
  124. AudacityProject &mProject;
  125. Tags *mTags;
  126. // project tag values that will be set in the actual project if the
  127. // import is successful
  128. #define field(n, t) bool have##n; t n
  129. struct
  130. {
  131. field(vpos, int);
  132. field(h, double);
  133. field(zoom, double);
  134. field(sel0, double);
  135. field(sel1, double);
  136. #ifdef EXPERIMENTAL_SPECTRAL_EDITING
  137. field(selLow, double) = SelectedRegion::UndefinedFrequency;
  138. field(selHigh, double) = SelectedRegion::UndefinedFrequency;
  139. #endif
  140. field(rate, double);
  141. field(snapto, bool);
  142. field(selectionformat, wxString);
  143. field(audiotimeformat, wxString);
  144. field(frequencyformat, wxString);
  145. field(bandwidthformat, wxString);
  146. } mProjectAttrs;
  147. #undef field
  148. typedef struct
  149. {
  150. WaveTrack *track;
  151. WaveClip *clip;
  152. FilePath blockFile;
  153. FilePath audioFile;
  154. sampleCount len;
  155. sampleFormat format;
  156. sampleCount origin;
  157. int channel;
  158. } fileinfo;
  159. std::vector<fileinfo> mFiles;
  160. sampleCount mTotalSamples;
  161. sampleFormat mFormat;
  162. unsigned long mNumChannels;
  163. stack mHandlers;
  164. wxString mParentTag;
  165. wxString mCurrentTag;
  166. const wxChar **mAttrs;
  167. wxFileName mProjDir;
  168. using BlockFileMap =
  169. std::map<wxString, std::pair<FilePath, std::shared_ptr<SampleBlock>>>;
  170. BlockFileMap mFileMap;
  171. WaveTrack *mWaveTrack;
  172. WaveClip *mClip;
  173. std::vector<WaveClip *> mClips;
  174. ProgressResult mUpdateResult;
  175. TranslatableString mErrorMsg;
  176. };
  177. AUPImportPlugin::AUPImportPlugin()
  178. : ImportPlugin(FileExtensions(exts.begin(), exts.end()))
  179. {
  180. static_assert(
  181. sizeof(long long) >= sizeof(uint64_t) &&
  182. sizeof(long) >= sizeof(uint32_t),
  183. "Assumptions about sizes in XMLValueChecker calls are invalid!");
  184. }
  185. AUPImportPlugin::~AUPImportPlugin()
  186. {
  187. }
  188. wxString AUPImportPlugin::GetPluginStringID()
  189. {
  190. return wxT("legacyaup");
  191. }
  192. TranslatableString AUPImportPlugin::GetPluginFormatDescription()
  193. {
  194. return DESC;
  195. }
  196. ImportHandle AUPImportPlugin::Open(const FilePath &fileName,
  197. AudacityProject *project)
  198. {
  199. auto handle = std::make_unique<AUPImportFileHandle>(fileName, project);
  200. if (!handle->Open())
  201. {
  202. // Error or not something that we recognize
  203. return nullptr;
  204. }
  205. return handle;
  206. }
  207. static Importer::RegisteredImportPlugin registered
  208. {
  209. "AUP", std::make_unique<AUPImportPlugin>()
  210. };
  211. AUPImportFileHandle::AUPImportFileHandle(const FilePath &fileName,
  212. AudacityProject *project)
  213. : ImportFileHandle(fileName),
  214. mProject(*project)
  215. {
  216. }
  217. AUPImportFileHandle::~AUPImportFileHandle()
  218. {
  219. }
  220. TranslatableString AUPImportFileHandle::GetFileDescription()
  221. {
  222. return DESC;
  223. }
  224. auto AUPImportFileHandle::GetFileUncompressedBytes() -> ByteCount
  225. {
  226. // TODO: Get Uncompressed byte count.
  227. return 0;
  228. }
  229. ProgressResult AUPImportFileHandle::Import(WaveTrackFactory *WXUNUSED(trackFactory),
  230. TrackHolders &WXUNUSED(outTracks),
  231. Tags *tags)
  232. {
  233. auto &history = ProjectHistory::Get(mProject);
  234. auto &tracks = TrackList::Get(mProject);
  235. auto &viewInfo = ViewInfo::Get(mProject);
  236. auto &settings = ProjectSettings::Get(mProject);
  237. auto &selman = ProjectSelectionManager::Get(mProject);
  238. auto oldNumTracks = tracks.size();
  239. auto cleanup = finally([this, &tracks, oldNumTracks]{
  240. if (mUpdateResult != ProgressResult::Success) {
  241. // Revoke additions of tracks
  242. while (oldNumTracks < tracks.size()) {
  243. Track *lastTrack = *tracks.Any().rbegin();
  244. tracks.Remove(lastTrack);
  245. }
  246. }
  247. });
  248. bool isDirty = history.GetDirty() || !tracks.empty();
  249. mTotalSamples = 0;
  250. mTags = tags;
  251. CreateProgress();
  252. mUpdateResult = ProgressResult::Success;
  253. XMLFileReader xmlFile;
  254. bool success = xmlFile.Parse(this, mFilename);
  255. if (!success)
  256. {
  257. AudacityMessageBox(
  258. XO("Couldn't import the project:\n\n%s").Format(xmlFile.GetErrorStr()),
  259. XO("Import Project"),
  260. wxOK | wxCENTRE,
  261. &GetProjectFrame(mProject));
  262. return ProgressResult::Failed;
  263. }
  264. if (!mErrorMsg.empty())
  265. {
  266. // Error or warning
  267. AudacityMessageBox(
  268. mErrorMsg,
  269. XO("Import Project"),
  270. wxOK | wxCENTRE,
  271. &GetProjectFrame(mProject));
  272. if (mUpdateResult == ProgressResult::Failed)
  273. {
  274. // Error
  275. return ProgressResult::Failed;
  276. }
  277. }
  278. // If mUpdateResult had been changed, we would have returned already
  279. wxASSERT( mUpdateResult == ProgressResult::Success );
  280. sampleCount processed = 0;
  281. for (auto fi : mFiles)
  282. {
  283. mUpdateResult = mProgress->Update(processed.as_long_long(), mTotalSamples.as_long_long());
  284. if (mUpdateResult != ProgressResult::Success)
  285. {
  286. return mUpdateResult;
  287. }
  288. mClip = fi.clip;
  289. mWaveTrack = fi.track;
  290. if (fi.blockFile.empty())
  291. {
  292. AddSilence(fi.len);
  293. }
  294. else
  295. {
  296. AddSamples(fi.blockFile, fi.audioFile,
  297. fi.len, fi.format, fi.origin, fi.channel);
  298. }
  299. processed += fi.len;
  300. }
  301. for (auto pClip : mClips)
  302. pClip->UpdateEnvelopeTrackLen();
  303. wxASSERT( mUpdateResult == ProgressResult::Success );
  304. // If the active project is "dirty", then bypass the below updates as we don't
  305. // want to going changing things the user may have already set up.
  306. if (isDirty)
  307. {
  308. return mUpdateResult;
  309. }
  310. if (mProjectAttrs.haverate)
  311. {
  312. auto &bar = SelectionBar::Get(mProject);
  313. bar.SetRate(mProjectAttrs.rate);
  314. }
  315. if (mProjectAttrs.havesnapto)
  316. {
  317. selman.AS_SetSnapTo(mProjectAttrs.snapto ? SNAP_NEAREST : SNAP_OFF);
  318. }
  319. if (mProjectAttrs.haveselectionformat)
  320. {
  321. selman.AS_SetSelectionFormat(NumericConverter::LookupFormat(NumericConverter::TIME, mProjectAttrs.selectionformat));
  322. }
  323. if (mProjectAttrs.haveaudiotimeformat)
  324. {
  325. selman.TT_SetAudioTimeFormat(NumericConverter::LookupFormat(NumericConverter::TIME, mProjectAttrs.audiotimeformat));
  326. }
  327. if (mProjectAttrs.havefrequencyformat)
  328. {
  329. selman.SSBL_SetFrequencySelectionFormatName(NumericConverter::LookupFormat(NumericConverter::TIME, mProjectAttrs.frequencyformat));
  330. }
  331. if (mProjectAttrs.havebandwidthformat)
  332. {
  333. selman.SSBL_SetBandwidthSelectionFormatName(NumericConverter::LookupFormat(NumericConverter::TIME, mProjectAttrs.bandwidthformat));
  334. }
  335. // PRL: It seems this must happen after SetSnapTo
  336. if (mProjectAttrs.havevpos)
  337. {
  338. viewInfo.vpos = mProjectAttrs.vpos;
  339. }
  340. if (mProjectAttrs.haveh)
  341. {
  342. viewInfo.h = mProjectAttrs.h;
  343. }
  344. if (mProjectAttrs.havezoom)
  345. {
  346. viewInfo.SetZoom(mProjectAttrs.zoom);
  347. }
  348. if (mProjectAttrs.havesel0)
  349. {
  350. viewInfo.selectedRegion.setT0(mProjectAttrs.sel0);
  351. }
  352. if (mProjectAttrs.havesel1)
  353. {
  354. viewInfo.selectedRegion.setT1(mProjectAttrs.sel1);
  355. }
  356. #ifdef EXPERIMENTAL_SPECTRAL_EDITING
  357. if (mProjectAttrs.haveselLow)
  358. {
  359. viewInfo.selectedRegion.setF0(mProjectAttrs.selLow);
  360. }
  361. if (mProjectAttrs.haveselHigh)
  362. {
  363. viewInfo.selectedRegion.setF1(mProjectAttrs.selHigh);
  364. }
  365. #endif
  366. return mUpdateResult;
  367. }
  368. wxInt32 AUPImportFileHandle::GetStreamCount()
  369. {
  370. return 1;
  371. }
  372. const TranslatableStrings &AUPImportFileHandle::GetStreamInfo()
  373. {
  374. static TranslatableStrings empty;
  375. return empty;
  376. }
  377. void AUPImportFileHandle::SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use))
  378. {
  379. }
  380. bool AUPImportFileHandle::Open()
  381. {
  382. wxFFile ff(mFilename, wxT("rb"));
  383. if (ff.IsOpened())
  384. {
  385. char buf[256];
  386. int numRead = ff.Read(buf, sizeof(buf));
  387. ff.Close();
  388. buf[sizeof(buf) - 1] = '\0';
  389. if (!wxStrncmp(buf, wxT("AudacityProject"), 15))
  390. {
  391. AudacityMessageBox(
  392. XO("This project was saved by Audacity version 1.0 or earlier. The format has\n"
  393. "changed and this version of Audacity is unable to import the project.\n\n"
  394. "Use a version of Audacity prior to v3.0.0 to upgrade the project and then\n"
  395. "you may import it with this version of Audacity."),
  396. XO("Import Project"),
  397. wxOK | wxCENTRE,
  398. &GetProjectFrame(mProject));
  399. return false;
  400. }
  401. if (wxStrncmp(buf, "<?xml", 5) == 0 &&
  402. (wxStrstr(buf, "<audacityproject") ||
  403. wxStrstr(buf, "<project") ))
  404. {
  405. return true;
  406. }
  407. }
  408. return false;
  409. }
  410. XMLTagHandler *AUPImportFileHandle::HandleXMLChild(const wxChar *tag)
  411. {
  412. return this;
  413. }
  414. void AUPImportFileHandle::HandleXMLEndTag(const wxChar *tag)
  415. {
  416. if (mUpdateResult != ProgressResult::Success)
  417. {
  418. return;
  419. }
  420. struct node node = mHandlers.back();
  421. if (wxStrcmp(tag, wxT("waveclip")) == 0)
  422. {
  423. mClip = nullptr;
  424. }
  425. if (node.handler)
  426. {
  427. node.handler->HandleXMLEndTag(tag);
  428. }
  429. mHandlers.pop_back();
  430. if (mHandlers.size())
  431. {
  432. node = mHandlers.back();
  433. mParentTag = node.parent;
  434. mCurrentTag = node.tag;
  435. }
  436. }
  437. bool AUPImportFileHandle::HandleXMLTag(const wxChar *tag, const wxChar **attrs)
  438. {
  439. if (mUpdateResult != ProgressResult::Success)
  440. {
  441. return false;
  442. }
  443. mParentTag = mCurrentTag;
  444. mCurrentTag = tag;
  445. mAttrs = attrs;
  446. XMLTagHandler *handler = nullptr;
  447. bool success = false;
  448. if (mCurrentTag.IsSameAs(wxT("project")) ||
  449. mCurrentTag.IsSameAs(wxT("audacityproject")))
  450. {
  451. success = HandleProject(handler);
  452. }
  453. else if (mCurrentTag.IsSameAs(wxT("labeltrack")))
  454. {
  455. success = HandleLabelTrack(handler);
  456. }
  457. else if (mCurrentTag.IsSameAs(wxT("notetrack")))
  458. {
  459. success = HandleNoteTrack(handler);
  460. }
  461. else if (mCurrentTag.IsSameAs(wxT("timetrack")))
  462. {
  463. success = HandleTimeTrack(handler);
  464. }
  465. else if (mCurrentTag.IsSameAs(wxT("wavetrack")))
  466. {
  467. success = HandleWaveTrack(handler);
  468. }
  469. else if (mCurrentTag.IsSameAs(wxT("tags")))
  470. {
  471. success = HandleTags(handler);
  472. }
  473. else if (mCurrentTag.IsSameAs(wxT("tag")))
  474. {
  475. success = HandleTag(handler);
  476. }
  477. else if (mCurrentTag.IsSameAs(wxT("label")))
  478. {
  479. success = HandleLabel(handler);
  480. }
  481. else if (mCurrentTag.IsSameAs(wxT("waveclip")))
  482. {
  483. success = HandleWaveClip(handler);
  484. }
  485. else if (mCurrentTag.IsSameAs(wxT("sequence")))
  486. {
  487. success = HandleSequence(handler);
  488. }
  489. else if (mCurrentTag.IsSameAs(wxT("waveblock")))
  490. {
  491. success = HandleWaveBlock(handler);
  492. }
  493. else if (mCurrentTag.IsSameAs(wxT("envelope")))
  494. {
  495. success = HandleEnvelope(handler);
  496. }
  497. else if (mCurrentTag.IsSameAs(wxT("controlpoint")))
  498. {
  499. success = HandleControlPoint(handler);
  500. }
  501. else if (mCurrentTag.IsSameAs(wxT("simpleblockfile")))
  502. {
  503. success = HandleSimpleBlockFile(handler);
  504. }
  505. else if (mCurrentTag.IsSameAs(wxT("silentblockfile")))
  506. {
  507. success = HandleSilentBlockFile(handler);
  508. }
  509. else if (mCurrentTag.IsSameAs(wxT("pcmaliasblockfile")))
  510. {
  511. success = HandlePCMAliasBlockFile(handler);
  512. }
  513. else if (mCurrentTag.IsSameAs(wxT("import")))
  514. {
  515. success = HandleImport(handler);
  516. }
  517. if (!success || (handler && !handler->HandleXMLTag(tag, attrs)))
  518. {
  519. return SetError(XO("Internal error in importer...tag not recognized"));
  520. }
  521. mHandlers.push_back({mParentTag, mCurrentTag, handler});
  522. return true;
  523. }
  524. bool AUPImportFileHandle::HandleProject(XMLTagHandler *&handler)
  525. {
  526. auto &fileMan = ProjectFileManager::Get(mProject);
  527. auto &window = GetProjectFrame(mProject);
  528. int requiredTags = 0;
  529. while (*mAttrs)
  530. {
  531. const wxChar *attr = *mAttrs++;
  532. const wxChar *value = *mAttrs++;
  533. double dValue;
  534. if (!value)
  535. {
  536. break;
  537. }
  538. if (!XMLValueChecker::IsGoodString(value))
  539. {
  540. return SetError(XO("Invalid project '%s' attribute.").Format(attr));
  541. }
  542. wxString strValue = value;
  543. #define set(f, v) (mProjectAttrs.have ## f = true, mProjectAttrs.f = v)
  544. // ViewInfo
  545. if (!wxStrcmp(attr, wxT("vpos")))
  546. {
  547. long lValue;
  548. if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue < 0))
  549. {
  550. return SetError(XO("Invalid project 'vpos' attribute."));
  551. }
  552. set(vpos, (int) lValue);
  553. }
  554. else if (!wxStrcmp(attr, wxT("h")))
  555. {
  556. if (!Internat::CompatibleToDouble(value, &dValue))
  557. {
  558. return SetError(XO("Invalid project 'h' attribute."));
  559. }
  560. set(h, dValue);
  561. }
  562. else if (!wxStrcmp(attr, wxT("zoom")))
  563. {
  564. if (!Internat::CompatibleToDouble(value, &dValue) || (dValue < 0.0))
  565. {
  566. return SetError(XO("Invalid project 'zoom' attribute."));
  567. }
  568. set(zoom, dValue);
  569. }
  570. // Viewinfo.SelectedRegion
  571. else if (!wxStrcmp(attr, wxT("sel0")))
  572. {
  573. if (!Internat::CompatibleToDouble(value, &dValue) || (dValue < 0.0))
  574. {
  575. return SetError(XO("Invalid project 'sel0' attribute."));
  576. }
  577. set(sel0, dValue);
  578. }
  579. else if (!wxStrcmp(attr, wxT("sel1")))
  580. {
  581. if (!Internat::CompatibleToDouble(value, &dValue) || (dValue < 0.0))
  582. {
  583. return SetError(XO("Invalid project 'sel1' attribute."));
  584. }
  585. set(sel1, dValue);
  586. }
  587. #ifdef EXPERIMENTAL_SPECTRAL_EDITING
  588. else if (!wxStrcmp(attr, wxT("selLow")))
  589. {
  590. if (!Internat::CompatibleToDouble(value, &dValue) || (dValue < 0.0))
  591. {
  592. return SetError(XO("Invalid project 'selLow' attribute."));
  593. }
  594. set(selLow, dValue);
  595. }
  596. else if (!wxStrcmp(attr, wxT("selHigh")))
  597. {
  598. if (!Internat::CompatibleToDouble(value, &dValue) || (dValue < 0.0))
  599. {
  600. return SetError(XO("Invalid project 'selHigh' attribute."));
  601. }
  602. set(selHigh, dValue);
  603. }
  604. #endif
  605. else if (!wxStrcmp(attr, wxT("version")))
  606. {
  607. requiredTags++;
  608. }
  609. else if (!wxStrcmp(attr, wxT("audacityversion")))
  610. {
  611. requiredTags++;
  612. }
  613. else if (!wxStrcmp(attr, wxT("projname")))
  614. {
  615. requiredTags++;
  616. mProjDir = mFilename;
  617. wxString altname = mProjDir.GetName() + wxT("_data");
  618. mProjDir.SetFullName(wxEmptyString);
  619. wxString projName = value;
  620. bool found = false;
  621. // First try to load the data files based on the _data dir given in the .aup file
  622. if (!projName.empty())
  623. {
  624. mProjDir.AppendDir(projName);
  625. if (!mProjDir.DirExists())
  626. {
  627. mProjDir.RemoveLastDir();
  628. projName.clear();
  629. }
  630. }
  631. // If that fails then try to use the filename of the .aup as the base directory
  632. // This is because unzipped projects e.g. those that get transferred between mac-pc
  633. // may have encoding issues and end up expanding the wrong filenames for certain
  634. // international characters (such as capital 'A' with an umlaut.)
  635. if (projName.empty())
  636. {
  637. projName = altname;
  638. mProjDir.AppendDir(projName);
  639. if (!mProjDir.DirExists())
  640. {
  641. projName.clear();
  642. }
  643. }
  644. // No luck...complain and bail
  645. if (projName.empty())
  646. {
  647. AudacityMessageBox(
  648. XO("Couldn't find the project data folder: \"%s\"").Format(value),
  649. XO("Error Opening Project"),
  650. wxOK | wxCENTRE,
  651. &window);
  652. return false;
  653. }
  654. // Collect and hash the file names within the project directory
  655. wxArrayString files;
  656. size_t cnt = wxDir::GetAllFiles(mProjDir.GetFullPath(),
  657. &files,
  658. "*.*");
  659. for (const auto &fn : files)
  660. {
  661. mFileMap[wxFileNameFromPath(fn)] = {fn, {}};
  662. }
  663. }
  664. else if (!wxStrcmp(attr, wxT("rate")))
  665. {
  666. if (!Internat::CompatibleToDouble(value, &dValue) || (dValue < 0.0))
  667. {
  668. return SetError(XO("Invalid project 'selLow' attribute."));
  669. }
  670. set(rate, dValue);
  671. }
  672. else if (!wxStrcmp(attr, wxT("snapto")))
  673. {
  674. set(snapto, (strValue == wxT("on") ? true : false));
  675. }
  676. else if (!wxStrcmp(attr, wxT("selectionformat")))
  677. {
  678. set(selectionformat, strValue);
  679. }
  680. else if (!wxStrcmp(attr, wxT("audiotimeformat")))
  681. {
  682. set(audiotimeformat, strValue);
  683. }
  684. else if (!wxStrcmp(attr, wxT("frequencyformat")))
  685. {
  686. set(frequencyformat, strValue);
  687. }
  688. else if (!wxStrcmp(attr, wxT("bandwidthformat")))
  689. {
  690. set(bandwidthformat, strValue);
  691. }
  692. #undef set
  693. }
  694. if (requiredTags < 3)
  695. {
  696. return false;
  697. }
  698. // Do not set the handler - already handled
  699. return true;
  700. }
  701. bool AUPImportFileHandle::HandleLabelTrack(XMLTagHandler *&handler)
  702. {
  703. handler = TrackList::Get(mProject).Add(std::make_shared<LabelTrack>());
  704. return true;
  705. }
  706. bool AUPImportFileHandle::HandleNoteTrack(XMLTagHandler *&handler)
  707. {
  708. #if defined(USE_MIDI)
  709. handler = TrackList::Get(mProject).Add(std::make_shared<NoteTrack>());
  710. return true;
  711. #else
  712. AudacityMessageBox(
  713. XO("MIDI tracks found in project file, but this build of Audacity does not include MIDI support, bypassing track."),
  714. XO("Project Import"),
  715. wxOK | wxICON_EXCLAMATION | wxCENTRE,
  716. &GetProjectFrame(mProject));
  717. return false;
  718. #endif
  719. }
  720. bool AUPImportFileHandle::HandleTimeTrack(XMLTagHandler *&handler)
  721. {
  722. auto &tracks = TrackList::Get(mProject);
  723. // Bypass this timetrack if the project already has one
  724. // (See HandleTimeEnvelope and HandleControlPoint also)
  725. if (*tracks.Any<TimeTrack>().begin())
  726. {
  727. AudacityMessageBox(
  728. XO("The active project already has a time track and one was encountered in the project being imported, bypassing imported time track."),
  729. XO("Project Import"),
  730. wxOK | wxICON_EXCLAMATION | wxCENTRE,
  731. &GetProjectFrame(mProject));
  732. return true;
  733. }
  734. auto &viewInfo = ViewInfo::Get( mProject );
  735. handler =
  736. TrackList::Get(mProject).Add(std::make_shared<TimeTrack>(&viewInfo));
  737. return true;
  738. }
  739. bool AUPImportFileHandle::HandleWaveTrack(XMLTagHandler *&handler)
  740. {
  741. auto &trackFactory = WaveTrackFactory::Get(mProject);
  742. handler = mWaveTrack =
  743. TrackList::Get(mProject).Add(trackFactory.NewWaveTrack());
  744. // No active clip. In early versions of Audacity, there was a single
  745. // implied clip so we'll create a clip when the first "sequence" is
  746. // found.
  747. mClip = nullptr;
  748. return true;
  749. }
  750. bool AUPImportFileHandle::HandleTags(XMLTagHandler *&handler)
  751. {
  752. wxString n;
  753. wxString v;
  754. // Support for legacy tags
  755. while(*mAttrs)
  756. {
  757. const wxChar *attr = *mAttrs++;
  758. const wxChar *value = *mAttrs++;
  759. if (!value)
  760. {
  761. break;
  762. }
  763. // Ignore empty tags
  764. if (!*value)
  765. {
  766. continue;
  767. }
  768. if (!XMLValueChecker::IsGoodString(attr) || !XMLValueChecker::IsGoodString(value))
  769. {
  770. // Log it???
  771. return false;
  772. }
  773. if (!wxStrcmp(attr, "id3v2"))
  774. {
  775. continue;
  776. }
  777. else if (!wxStrcmp(attr, "track"))
  778. {
  779. n = wxT("TRACKNUMBER");
  780. }
  781. else
  782. {
  783. n = attr;
  784. n.MakeUpper();
  785. }
  786. mTags->SetTag(n, value);
  787. }
  788. // Do not set the handler - already handled
  789. return true;
  790. }
  791. bool AUPImportFileHandle::HandleTag(XMLTagHandler *&handler)
  792. {
  793. if (!mParentTag.IsSameAs(wxT("tags")))
  794. {
  795. return false;
  796. }
  797. wxString n, v;
  798. while (*mAttrs)
  799. {
  800. wxString attr = *mAttrs++;
  801. if (attr.empty())
  802. {
  803. break;
  804. }
  805. wxString value = *mAttrs++;
  806. if (!XMLValueChecker::IsGoodString(attr) || !XMLValueChecker::IsGoodString(value))
  807. {
  808. break;
  809. }
  810. if (attr == wxT("name"))
  811. {
  812. n = value;
  813. }
  814. else if (attr == wxT("value"))
  815. {
  816. v = value;
  817. }
  818. }
  819. if (n == wxT("id3v2"))
  820. {
  821. // LLL: This is obsolete, but it must be handled and ignored.
  822. }
  823. else
  824. {
  825. mTags->SetTag(n, v);
  826. }
  827. // Do not set the handler - already handled
  828. return true;
  829. }
  830. bool AUPImportFileHandle::HandleLabel(XMLTagHandler *&handler)
  831. {
  832. if (!mParentTag.IsSameAs(wxT("labeltrack")))
  833. {
  834. return false;
  835. }
  836. // The parent handler also handles this tag
  837. handler = mHandlers.back().handler;
  838. return true;
  839. }
  840. bool AUPImportFileHandle::HandleWaveClip(XMLTagHandler *&handler)
  841. {
  842. struct node node = mHandlers.back();
  843. if (mParentTag.IsSameAs(wxT("wavetrack")))
  844. {
  845. WaveTrack *wavetrack = static_cast<WaveTrack *>(node.handler);
  846. handler = wavetrack->CreateClip();
  847. }
  848. else if (mParentTag.IsSameAs(wxT("waveclip")))
  849. {
  850. // Nested wave clips are cut lines
  851. WaveClip *waveclip = static_cast<WaveClip *>(node.handler);
  852. handler = waveclip->HandleXMLChild(mCurrentTag);
  853. }
  854. mClip = static_cast<WaveClip *>(handler);
  855. mClips.push_back(mClip);
  856. return true;
  857. }
  858. bool AUPImportFileHandle::HandleEnvelope(XMLTagHandler *&handler)
  859. {
  860. struct node node = mHandlers.back();
  861. if (mParentTag.IsSameAs(wxT("timetrack")))
  862. {
  863. // If an imported timetrack was bypassed, then we want to bypass the
  864. // envelope as well. (See HandleTimeTrack and HandleControlPoint)
  865. if (node.handler)
  866. {
  867. TimeTrack *timetrack = static_cast<TimeTrack *>(node.handler);
  868. handler = timetrack->GetEnvelope();
  869. }
  870. }
  871. // Earlier versions of Audacity had a single implied waveclip, so for
  872. // these versions, we get or create the only clip in the track.
  873. else if (mParentTag.IsSameAs(wxT("wavetrack")))
  874. {
  875. handler = mWaveTrack->RightmostOrNewClip()->GetEnvelope();
  876. }
  877. // Nested wave clips are cut lines
  878. else if (mParentTag.IsSameAs(wxT("waveclip")))
  879. {
  880. WaveClip *waveclip = static_cast<WaveClip *>(node.handler);
  881. handler = waveclip->GetEnvelope();
  882. }
  883. return true;
  884. }
  885. bool AUPImportFileHandle::HandleControlPoint(XMLTagHandler *&handler)
  886. {
  887. struct node node = mHandlers.back();
  888. if (mParentTag.IsSameAs(wxT("envelope")))
  889. {
  890. // If an imported timetrack was bypassed, then we want to bypass the
  891. // control points as well. (See HandleTimeTrack and HandleEnvelope)
  892. if (node.handler)
  893. {
  894. Envelope *envelope = static_cast<Envelope *>(node.handler);
  895. handler = envelope->HandleXMLChild(mCurrentTag);
  896. }
  897. }
  898. return true;
  899. }
  900. bool AUPImportFileHandle::HandleSequence(XMLTagHandler *&handler)
  901. {
  902. struct node node = mHandlers.back();
  903. WaveClip *waveclip = static_cast<WaveClip *>(node.handler);
  904. // Earlier versions of Audacity had a single implied waveclip, so for
  905. // these versions, we get or create the only clip in the track.
  906. if (mParentTag.IsSameAs(wxT("wavetrack")))
  907. {
  908. XMLTagHandler *dummy;
  909. HandleWaveClip(dummy);
  910. waveclip = mClip;
  911. }
  912. while(*mAttrs)
  913. {
  914. const wxChar *attr = *mAttrs++;
  915. const wxChar *value = *mAttrs++;
  916. if (!value)
  917. {
  918. break;
  919. }
  920. const wxString strValue = value; // promote string, we need this for all
  921. if (!wxStrcmp(attr, wxT("maxsamples")))
  922. {
  923. // This attribute is a sample count, so can be 64bit
  924. long long llvalue;
  925. if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&llvalue) || (llvalue < 0))
  926. {
  927. return SetError(XO("Invalid sequence 'maxsamples' attribute."));
  928. }
  929. // Dominic, 12/10/2006:
  930. // Let's check that maxsamples is >= 1024 and <= 64 * 1024 * 1024
  931. // - that's a pretty wide range of reasonable values.
  932. if ((llvalue < 1024) || (llvalue > 64 * 1024 * 1024))
  933. {
  934. return SetError(XO("Invalid sequence 'maxsamples' attribute."));
  935. }
  936. }
  937. else if (!wxStrcmp(attr, wxT("sampleformat")))
  938. {
  939. // This attribute is a sample format, normal int
  940. long fValue;
  941. if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&fValue) || (fValue < 0) || !XMLValueChecker::IsValidSampleFormat(fValue))
  942. {
  943. return SetError(XO("Invalid sequence 'sampleformat' attribute."));
  944. }
  945. mFormat = (sampleFormat) fValue;
  946. waveclip->GetSequence()->ConvertToSampleFormat( mFormat );
  947. }
  948. else if (!wxStrcmp(attr, wxT("numsamples")))
  949. {
  950. // This attribute is a sample count, so can be 64bit
  951. long long llvalue;
  952. if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&llvalue) || (llvalue < 0))
  953. {
  954. return SetError(XO("Invalid sequence 'numsamples' attribute."));
  955. }
  956. }
  957. }
  958. // Do not set the handler - already handled
  959. return true;
  960. }
  961. bool AUPImportFileHandle::HandleWaveBlock(XMLTagHandler *&handler)
  962. {
  963. while(*mAttrs)
  964. {
  965. const wxChar *attr = *mAttrs++;
  966. const wxChar *value = *mAttrs++;
  967. if (!value)
  968. {
  969. break;
  970. }
  971. const wxString strValue = value;
  972. if (!wxStrcmp(attr, wxT("start")))
  973. {
  974. // making sure that values > 2^31 are OK because long clips will need them.
  975. long long llvalue;
  976. if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&llvalue) || (llvalue < 0))
  977. {
  978. return SetError(XO("Unable to parse the waveblock 'start' attribute"));
  979. }
  980. }
  981. }
  982. // Do not set the handler - already handled
  983. return true;
  984. }
  985. bool AUPImportFileHandle::HandleSimpleBlockFile(XMLTagHandler *&handler)
  986. {
  987. FilePath filename;
  988. size_t len = 0;
  989. while (*mAttrs)
  990. {
  991. const wxChar *attr = *mAttrs++;
  992. const wxChar *value = *mAttrs++;
  993. if (!value)
  994. {
  995. break;
  996. }
  997. const wxString strValue = value;
  998. // Can't use XMLValueChecker::IsGoodFileName here, but do part of its test.
  999. if (!wxStricmp(attr, wxT("filename")))
  1000. {
  1001. if (XMLValueChecker::IsGoodFileString(strValue))
  1002. {
  1003. if (mFileMap.find(strValue) != mFileMap.end())
  1004. {
  1005. filename = mFileMap[strValue].first;
  1006. }
  1007. else
  1008. {
  1009. SetWarning(XO("Missing project file %s\n\nInserting silence instead.")
  1010. .Format(strValue));
  1011. }
  1012. }
  1013. }
  1014. else if (!wxStrcmp(attr, wxT("len")))
  1015. {
  1016. long lValue;
  1017. if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue <= 0))
  1018. {
  1019. return SetError(XO("Missing or invalid simpleblockfile 'len' attribute."));
  1020. }
  1021. len = lValue;
  1022. }
  1023. }
  1024. // Do not set the handler - already handled
  1025. AddFile(len, mFormat, filename, filename);
  1026. return true;
  1027. }
  1028. bool AUPImportFileHandle::HandleSilentBlockFile(XMLTagHandler *&handler)
  1029. {
  1030. FilePath filename;
  1031. size_t len = 0;
  1032. while (*mAttrs)
  1033. {
  1034. const wxChar *attr = *mAttrs++;
  1035. const wxChar *value = *mAttrs++;
  1036. if (!value)
  1037. {
  1038. break;
  1039. }
  1040. const wxString strValue = value;
  1041. if (!wxStrcmp(attr, wxT("len")))
  1042. {
  1043. long lValue;
  1044. if (!XMLValueChecker::IsGoodInt(value) || !strValue.ToLong(&lValue) || !(lValue > 0))
  1045. {
  1046. return SetError(XO("Missing or invalid silentblockfile 'len' attribute."));
  1047. }
  1048. len = lValue;
  1049. }
  1050. }
  1051. // Do not set the handler - already handled
  1052. AddFile(len, mFormat);
  1053. return true;
  1054. }
  1055. bool AUPImportFileHandle::HandlePCMAliasBlockFile(XMLTagHandler *&handler)
  1056. {
  1057. wxString summaryFilename;
  1058. wxFileName filename;
  1059. sampleCount start = 0;
  1060. size_t len = 0;
  1061. int channel = 0;
  1062. wxString name;
  1063. while (*mAttrs)
  1064. {
  1065. const wxChar *attr = *mAttrs++;
  1066. const wxChar *value = *mAttrs++;
  1067. if (!value)
  1068. {
  1069. break;
  1070. }
  1071. const wxString strValue = value;
  1072. if (!wxStricmp(attr, wxT("aliasfile")))
  1073. {
  1074. if (XMLValueChecker::IsGoodPathName(strValue))
  1075. {
  1076. filename.Assign(strValue);
  1077. }
  1078. else if (XMLValueChecker::IsGoodFileName(strValue, mProjDir.GetPath()))
  1079. {
  1080. // Allow fallback of looking for the file name, located in the data directory.
  1081. filename.Assign(mProjDir.GetPath(), strValue);
  1082. }
  1083. else if (XMLValueChecker::IsGoodPathString(strValue))
  1084. {
  1085. // If the aliased file is missing, we failed XMLValueChecker::IsGoodPathName()
  1086. // and XMLValueChecker::IsGoodFileName, because both do existence tests.
  1087. SetWarning(XO("Missing alias file %s\n\nInserting silence instead.")
  1088. .Format(strValue));
  1089. }
  1090. }
  1091. else if (!wxStricmp(attr, wxT("summaryfile")))
  1092. {
  1093. summaryFilename = strValue;
  1094. }
  1095. else if (!wxStricmp(attr, wxT("aliasstart")))
  1096. {
  1097. long long llValue;
  1098. if (!XMLValueChecker::IsGoodInt64(strValue) || !strValue.ToLongLong(&llValue) || (llValue < 0))
  1099. {
  1100. return SetError(XO("Missing or invalid pcmaliasblockfile 'aliasstart' attribute."));
  1101. }
  1102. start = llValue;
  1103. }
  1104. else if (!wxStricmp(attr, wxT("aliaslen")))
  1105. {
  1106. long lValue;
  1107. if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue <= 0))
  1108. {
  1109. return SetError(XO("Missing or invalid pcmaliasblockfile 'aliaslen' attribute."));
  1110. }
  1111. len = lValue;
  1112. }
  1113. else if (!wxStricmp(attr, wxT("aliaschannel")))
  1114. {
  1115. long lValue;
  1116. if (!XMLValueChecker::IsGoodInt(strValue) || !strValue.ToLong(&lValue) || (lValue < 0))
  1117. {
  1118. return SetError(XO("Missing or invalid pcmaliasblockfile 'aliaslen' attribute."));
  1119. }
  1120. channel = lValue;
  1121. }
  1122. }
  1123. // Do not set the handler - already handled
  1124. if (filename.IsOk())
  1125. AddFile(len, mFormat,
  1126. summaryFilename, filename.GetFullPath(),
  1127. start, channel);
  1128. else
  1129. AddFile(len, mFormat); // will add silence instead
  1130. return true;
  1131. }
  1132. bool AUPImportFileHandle::HandleImport(XMLTagHandler *&handler)
  1133. {
  1134. // Adapted from ImportXMLTagHandler::HandleXMLTag as in version 2.4.2
  1135. if (!mAttrs || !(*mAttrs) || wxStrcmp(*mAttrs++, wxT("filename")))
  1136. return false;
  1137. wxString strAttr = *mAttrs;
  1138. if (!XMLValueChecker::IsGoodPathName(strAttr))
  1139. {
  1140. // Maybe strAttr is just a fileName, not the full path. Try the project data directory.
  1141. wxFileNameWrapper fileName0{ mFilename };
  1142. fileName0.SetExt({});
  1143. wxFileNameWrapper fileName{
  1144. fileName0.GetFullPath() + wxT("_data"), strAttr };
  1145. if (XMLValueChecker::IsGoodFileName(strAttr, fileName.GetPath(wxPATH_GET_VOLUME)))
  1146. strAttr = fileName.GetFullPath();
  1147. else
  1148. {
  1149. wxLogWarning(wxT("Could not import file: %s"), strAttr);
  1150. return false;
  1151. }
  1152. }
  1153. auto &tracks = TrackList::Get(mProject);
  1154. auto oldNumTracks = tracks.size();
  1155. Track *pLast = nullptr;
  1156. if (oldNumTracks > 0)
  1157. pLast = *tracks.Any().rbegin();
  1158. // Guard this call so that C++ exceptions don't propagate through
  1159. // the expat library
  1160. GuardedCall(
  1161. [&] {
  1162. ProjectFileManager::Get( mProject ).Import(strAttr, false); },
  1163. [&] (AudacityException*) {}
  1164. );
  1165. if (oldNumTracks == tracks.size())
  1166. return false;
  1167. // Handle other attributes, now that we have the tracks.
  1168. // Apply them to all new wave tracks.
  1169. ++mAttrs;
  1170. const wxChar** pAttr;
  1171. bool bSuccess = true;
  1172. auto range = tracks.Any();
  1173. if (pLast) {
  1174. range = range.StartingWith(pLast);
  1175. ++range.first;
  1176. }
  1177. for (auto pTrack: range.Filter<WaveTrack>())
  1178. {
  1179. // Most of the "import" tag attributes are the same as for "wavetrack" tags,
  1180. // so apply them via WaveTrack::HandleXMLTag().
  1181. bSuccess = pTrack->HandleXMLTag(wxT("wavetrack"), mAttrs);
  1182. // "offset" tag is ignored in WaveTrack::HandleXMLTag except for legacy projects,
  1183. // so handle it here.
  1184. double dblValue;
  1185. pAttr = mAttrs;
  1186. while (*pAttr)
  1187. {
  1188. const wxChar *attr = *pAttr++;
  1189. const wxChar *value = *pAttr++;
  1190. const wxString strValue = value;
  1191. if (!wxStrcmp(attr, wxT("offset")) &&
  1192. XMLValueChecker::IsGoodString(strValue) &&
  1193. Internat::CompatibleToDouble(strValue, &dblValue))
  1194. pTrack->SetOffset(dblValue);
  1195. }
  1196. }
  1197. return bSuccess;
  1198. }
  1199. void AUPImportFileHandle::AddFile(sampleCount len,
  1200. sampleFormat format,
  1201. const FilePath &blockFilename /* = wxEmptyString */,
  1202. const FilePath &audioFilename /* = wxEmptyString */,
  1203. sampleCount origin /* = 0 */,
  1204. int channel /* = 0 */)
  1205. {
  1206. fileinfo fi = {};
  1207. fi.track = mWaveTrack;
  1208. fi.clip = mClip;
  1209. fi.blockFile = blockFilename;
  1210. fi.audioFile = audioFilename;
  1211. fi.len = len;
  1212. fi.format = format,
  1213. fi.origin = origin,
  1214. fi.channel = channel;
  1215. mFiles.push_back(fi);
  1216. mTotalSamples += len;
  1217. }
  1218. bool AUPImportFileHandle::AddSilence(sampleCount len)
  1219. {
  1220. wxASSERT(mClip || mWaveTrack);
  1221. if (mClip)
  1222. {
  1223. mClip->InsertSilence(mClip->GetEndTime(), mWaveTrack->LongSamplesToTime(len));
  1224. }
  1225. else if (mWaveTrack)
  1226. {
  1227. mWaveTrack->InsertSilence(mWaveTrack->GetEndTime(), mWaveTrack->LongSamplesToTime(len));
  1228. }
  1229. return true;
  1230. }
  1231. // All errors that occur here will simply insert silence and allow the
  1232. // import to continue.
  1233. bool AUPImportFileHandle::AddSamples(const FilePath &blockFilename,
  1234. const FilePath &audioFilename,
  1235. sampleCount len,
  1236. sampleFormat format,
  1237. sampleCount origin /* = 0 */,
  1238. int channel /* = 0 */)
  1239. {
  1240. auto pClip = mClip ? mClip : mWaveTrack->RightmostOrNewClip();
  1241. auto &pBlock = mFileMap[wxFileNameFromPath(blockFilename)].second;
  1242. if (pBlock) {
  1243. // Replicate the sharing of blocks
  1244. pClip->AppendSharedBlock( pBlock );
  1245. return true;
  1246. }
  1247. // Third party library has its own type alias, check it before
  1248. // adding origin + size_t
  1249. static_assert(sizeof(sampleCount::type) <= sizeof(sf_count_t),
  1250. "Type sf_count_t is too narrow to hold a sampleCount");
  1251. SF_INFO info;
  1252. memset(&info, 0, sizeof(info));
  1253. wxFile f; // will be closed when it goes out of scope
  1254. SNDFILE *sf = nullptr;
  1255. bool success = false;
  1256. auto cleanup = finally([&]
  1257. {
  1258. // Do this before any throwing might happen
  1259. if (sf)
  1260. {
  1261. SFCall<int>(sf_close, sf);
  1262. }
  1263. if (!success)
  1264. {
  1265. SetWarning(XO("Error while processing %s\n\nInserting silence.").Format(audioFilename));
  1266. // If we are unwinding for an exception, don't do another
  1267. // potentially throwing operation
  1268. if (std::uncaught_exceptions() == 0)
  1269. // If this does throw, let that propagate, don't guard the call
  1270. AddSilence(len);
  1271. }
  1272. });
  1273. if (!f.Open(audioFilename))
  1274. {
  1275. SetWarning(XO("Failed to open %s").Format(audioFilename));
  1276. return true;
  1277. }
  1278. // Even though there is an sf_open() that takes a filename, use the one that
  1279. // takes a file descriptor since wxWidgets can open a file with a Unicode name and
  1280. // libsndfile can't (under Windows).
  1281. sf = SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, FALSE);
  1282. if (!sf)
  1283. {
  1284. SetWarning(XO("Failed to open %s").Format(audioFilename));
  1285. return true;
  1286. }
  1287. if (origin > 0)
  1288. {
  1289. if (SFCall<sf_count_t>(sf_seek, sf, origin.as_long_long(), SEEK_SET) < 0)
  1290. {
  1291. SetWarning(XO("Failed to seek to position %lld in %s")
  1292. .Format(origin.as_long_long(), audioFilename));
  1293. return true;
  1294. }
  1295. }
  1296. sf_count_t cnt = len.as_size_t();
  1297. int channels = info.channels;
  1298. wxASSERT(channels >= 1);
  1299. wxASSERT(channel < channels);
  1300. SampleBuffer buffer(cnt, format);
  1301. samplePtr bufptr = buffer.ptr();
  1302. size_t framesRead = 0;
  1303. // These cases preserve the logic formerly in BlockFile.cpp,
  1304. // which was deleted at commit 98d1468.
  1305. if (channels == 1 && format == int16Sample && sf_subtype_is_integer(info.format))
  1306. {
  1307. // If both the src and dest formats are integer formats,
  1308. // read integers directly from the file, conversions not needed
  1309. framesRead = SFCall<sf_count_t>(sf_readf_short, sf, (short *) bufptr, cnt);
  1310. }
  1311. else if (channels == 1 && format == int24Sample && sf_subtype_is_integer(info.format))
  1312. {
  1313. framesRead = SFCall<sf_count_t>(sf_readf_int, sf, (int *) bufptr, cnt);
  1314. if (framesRead != cnt)
  1315. {
  1316. SetWarning(XO("Unable to read %lld samples from %s")
  1317. .Format(cnt, audioFilename));
  1318. return true;
  1319. }
  1320. // libsndfile gave us the 3 byte sample in the 3 most
  1321. // significant bytes -- we want it in the 3 least
  1322. // significant bytes.
  1323. int *intPtr = (int *) bufptr;
  1324. for (size_t i = 0; i < framesRead; i++)
  1325. {
  1326. intPtr[i] = intPtr[i] >> 8;
  1327. }
  1328. }
  1329. else if (format == int16Sample && !sf_subtype_more_than_16_bits(info.format))
  1330. {
  1331. // Special case: if the file is in 16-bit (or less) format,
  1332. // and the calling method wants 16-bit data, go ahead and
  1333. // read 16-bit data directly. This is a pretty common
  1334. // case, as most audio files are 16-bit.
  1335. SampleBuffer temp(cnt * channels, int16Sample);
  1336. short *tmpptr = (short *) temp.ptr();
  1337. framesRead = SFCall<sf_count_t>(sf_readf_short, sf, tmpptr, cnt);
  1338. if (framesRead != cnt)
  1339. {
  1340. SetWarning(XO("Unable to read %lld samples from %s")
  1341. .Format(cnt, audioFilename));
  1342. return true;
  1343. }
  1344. for (size_t i = 0; i < framesRead; i++)
  1345. {
  1346. ((short *)bufptr)[i] = tmpptr[(channels * i) + channel];
  1347. }
  1348. }
  1349. else
  1350. {
  1351. /*
  1352. Therefore none of the three cases above:
  1353. !(channels == 1 && format == int16Sample && sf_subtype_is_integer(info.format))
  1354. &&
  1355. !(channels == 1 && format == int24Sample && sf_subtype_is_integer(info.format))
  1356. &&
  1357. !(format == int16Sample && !sf_subtype_more_than_16_bits(info.format))
  1358. So format is not 16 bits with wider file format (third conjunct),
  1359. but still maybe it is 24 bits with float file format (second conjunct).
  1360. */
  1361. // Otherwise, let libsndfile handle the conversion and
  1362. // scaling, and pass us normalized data as floats. We can
  1363. // then convert to whatever format we want.
  1364. SampleBuffer tmpbuf(cnt * channels, floatSample);
  1365. float *tmpptr = (float *) tmpbuf.ptr();
  1366. framesRead = SFCall<sf_count_t>(sf_readf_float, sf, tmpptr, cnt);
  1367. if (framesRead != cnt)
  1368. {
  1369. SetWarning(XO("Unable to read %lld samples from %s")
  1370. .Format(cnt, audioFilename));
  1371. return true;
  1372. }
  1373. /*
  1374. Dithering will happen in CopySamples if format is 24 bits.
  1375. Should that be done?
  1376. Either the file is an ordinary simple block file -- and presumably the
  1377. track was saved specifying a matching format, so format is float and
  1378. there is no dithering.
  1379. Or else this is the very unusual case of an .auf file, importing PCM data
  1380. on demand. The destination format is narrower, requiring dither, only
  1381. if the user also specified a narrow format for the track. In such a
  1382. case, dithering is right.
  1383. */
  1384. CopySamples((samplePtr)(tmpptr + channel),
  1385. floatSample,
  1386. bufptr,
  1387. format,
  1388. framesRead,
  1389. gHighQualityDither /* high quality by default */,
  1390. channels /* source stride */);
  1391. }
  1392. wxASSERT(mClip || mWaveTrack);
  1393. // Add the samples to the clip/track
  1394. if (pClip)
  1395. {
  1396. pBlock = pClip->AppendNewBlock(bufptr, format, cnt);
  1397. }
  1398. // Let the finally block know everything is good
  1399. success = true;
  1400. return true;
  1401. }
  1402. bool AUPImportFileHandle::SetError(const TranslatableString &msg)
  1403. {
  1404. wxLogError(msg.Debug());
  1405. if (mErrorMsg.empty() || mUpdateResult == ProgressResult::Success)
  1406. {
  1407. mErrorMsg = msg;
  1408. }
  1409. // The only place where mUpdateResult is set during XML handling callbacks
  1410. mUpdateResult = ProgressResult::Failed;
  1411. return false;
  1412. }
  1413. bool AUPImportFileHandle::SetWarning(const TranslatableString &msg)
  1414. {
  1415. wxLogWarning(msg.Debug());
  1416. if (mErrorMsg.empty())
  1417. {
  1418. mErrorMsg = msg;
  1419. }
  1420. return false;
  1421. }