Audacity  2.2.2
Export.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  Export.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//****************************************************************//****************************************************************//****************************************************************//********************************************************************/
30 
31 #include "../Audacity.h"
32 #include "Export.h"
33 
34 #include <wx/dynarray.h>
35 #include <wx/file.h>
36 #include <wx/filename.h>
37 #include <wx/progdlg.h>
38 #include <wx/sizer.h>
39 #include <wx/slider.h>
40 #include <wx/statbox.h>
41 #include <wx/stattext.h>
42 #include <wx/string.h>
43 #include <wx/textctrl.h>
44 #include <wx/timer.h>
45 #include <wx/dcmemory.h>
46 #include <wx/window.h>
47 
48 #include "ExportPCM.h"
49 #include "ExportMP3.h"
50 #include "ExportOGG.h"
51 #include "ExportFLAC.h"
52 #include "ExportCL.h"
53 #include "ExportMP2.h"
54 #include "ExportFFmpeg.h"
55 
56 #include "sndfile.h"
57 
58 #include "FileDialog.h"
59 
60 #include "../DirManager.h"
61 #include "../FileFormats.h"
62 #include "../Internat.h"
63 #include "../Mix.h"
64 #include "../Prefs.h"
65 #include "../Project.h"
66 #include "../ShuttleGui.h"
67 #include "../WaveTrack.h"
68 #include "../widgets/ErrorDialog.h"
69 #include "../widgets/Warning.h"
70 #include "../AColor.h"
71 #include "../Dependencies.h"
72 #include "../FileNames.h"
73 
74 //----------------------------------------------------------------------------
75 // ExportPlugin
76 //----------------------------------------------------------------------------
77 #include <wx/arrimpl.cpp>
78 
79 WX_DEFINE_USER_EXPORTED_OBJARRAY(FormatInfoArray);
80 
82 {
83  mFormatInfos.Empty();
84 }
85 
87 {
88  mFormatInfos.Clear();
89 }
90 
91 bool ExportPlugin::CheckFileName(wxFileName & WXUNUSED(filename), int WXUNUSED(format))
92 {
93  return true;
94 }
95 
104 {
105  FormatInfo nf;
106  mFormatInfos.Add(nf);
107  return mFormatInfos.Count();
108 }
109 
111 {
112  return mFormatInfos.Count();
113 }
114 
119 void ExportPlugin::SetFormat(const wxString & format, int index)
120 {
121  mFormatInfos[index].mFormat = format;
122 }
123 
124 void ExportPlugin::SetDescription(const wxString & description, int index)
125 {
126  mFormatInfos[index].mDescription = description;
127 }
128 
129 void ExportPlugin::AddExtension(const wxString &extension,int index)
130 {
131  mFormatInfos[index].mExtensions.Add(extension);
132 }
133 
134 void ExportPlugin::SetExtensions(const wxArrayString & extensions, int index)
135 {
136  mFormatInfos[index].mExtensions = extensions;
137 }
138 
139 void ExportPlugin::SetMask(const wxString & mask, int index)
140 {
141  mFormatInfos[index].mMask = mask;
142 }
143 
144 void ExportPlugin::SetMaxChannels(unsigned maxchannels, unsigned index)
145 {
146  mFormatInfos[index].mMaxChannels = maxchannels;
147 }
148 
149 void ExportPlugin::SetCanMetaData(bool canmetadata, int index)
150 {
151  mFormatInfos[index].mCanMetaData = canmetadata;
152 }
153 
154 wxString ExportPlugin::GetFormat(int index)
155 {
156  return mFormatInfos[index].mFormat;
157 }
158 
159 wxString ExportPlugin::GetDescription(int index)
160 {
161  return mFormatInfos[index].mDescription;
162 }
163 
164 wxString ExportPlugin::GetExtension(int index)
165 {
166  return mFormatInfos[index].mExtensions[0];
167 }
168 
169 wxArrayString ExportPlugin::GetExtensions(int index)
170 {
171  return mFormatInfos[index].mExtensions;
172 }
173 
174 wxString ExportPlugin::GetMask(int index)
175 {
176  if (!mFormatInfos[index].mMask.IsEmpty()) {
177  return mFormatInfos[index].mMask;
178  }
179 
180  wxString mask = GetDescription(index) + wxT("|");
181 
182  // Build the mask
183  // wxString ext = GetExtension(index);
184  wxArrayString exts = GetExtensions(index);
185  for (size_t i = 0; i < exts.GetCount(); i++) {
186  mask += wxT("*.") + exts[i] + wxT(";");
187  }
188 
189  return mask;
190 }
191 
192 unsigned ExportPlugin::GetMaxChannels(int index)
193 {
194  return mFormatInfos[index].mMaxChannels;
195 }
196 
198 {
199  return mFormatInfos[index].mCanMetaData;
200 }
201 
202 bool ExportPlugin::IsExtension(const wxString & ext, int index)
203 {
204  bool isext = false;
205  for (int i = index; i < GetFormatCount(); i = GetFormatCount())
206  {
207  wxString defext = GetExtension(i);
208  wxArrayString defexts = GetExtensions(i);
209  int indofext = defexts.Index(ext, false);
210  if (defext == wxT("") || (indofext != wxNOT_FOUND))
211  isext = true;
212  }
213  return isext;
214 }
215 
216 bool ExportPlugin::DisplayOptions(wxWindow * WXUNUSED(parent), int WXUNUSED(format))
217 {
218  return false;
219 }
220 
221 wxWindow *ExportPlugin::OptionsCreate(wxWindow *parent, int WXUNUSED(format))
222 {
223  wxASSERT(parent); // To justify safenew
224  wxPanel *p = safenew wxPanelWrapper(parent, wxID_ANY);
226 
227  S.StartHorizontalLay(wxCENTER);
228  {
229  S.StartHorizontalLay(wxCENTER, 0);
230  {
231  S.Prop(1).AddTitle(_("No format specific options"));
232  }
233  S.EndHorizontalLay();
234  }
235  S.EndHorizontalLay();
236 
237  return p;
238 }
239 
240 //Create a mixer by computing the time warp factor
241 std::unique_ptr<Mixer> ExportPlugin::CreateMixer(const WaveTrackConstArray &inputTracks,
242  const TimeTrack *timeTrack,
243  double startTime, double stopTime,
244  unsigned numOutChannels, size_t outBufferSize, bool outInterleaved,
245  double outRate, sampleFormat outFormat,
246  bool highQuality, MixerSpec *mixerSpec)
247 {
248  // MB: the stop time should not be warped, this was a bug.
249  return std::make_unique<Mixer>(inputTracks,
250  // Throw, to stop exporting, if read fails:
251  true,
252  Mixer::WarpOptions(timeTrack),
253  startTime, stopTime,
254  numOutChannels, outBufferSize, outInterleaved,
255  outRate, outFormat,
256  highQuality, mixerSpec);
257 }
258 
259 void ExportPlugin::InitProgress(std::unique_ptr<ProgressDialog> &pDialog,
260  const wxString &title, const wxString &message)
261 {
262  if (!pDialog)
263  pDialog = std::make_unique<ProgressDialog>( title, message );
264  else {
265  pDialog->SetTitle( title );
266  pDialog->SetMessage( message );
267  pDialog->Reinit();
268  }
269 }
270 
271 //----------------------------------------------------------------------------
272 // Export
273 //----------------------------------------------------------------------------
274 
275 BEGIN_EVENT_TABLE(Exporter, wxEvtHandler)
276  EVT_FILECTRL_FILTERCHANGED(wxID_ANY, Exporter::OnFilterChanged)
278 
280 {
281  mMixerSpec = NULL;
282  mBook = NULL;
283  mFormatName = "";
284 
285  SetFileDialogTitle( _("Export Audio") );
286 
287  RegisterPlugin(New_ExportPCM());
288  RegisterPlugin(New_ExportMP3());
289 
290 #ifdef USE_LIBVORBIS
291  RegisterPlugin(New_ExportOGG());
292 #endif
293 
294 #ifdef USE_LIBFLAC
295  RegisterPlugin(New_ExportFLAC());
296 #endif
297 
298 #if USE_LIBTWOLAME
299  RegisterPlugin(New_ExportMP2());
300 #endif
301 
302  // Command line export not available on Windows and Mac platforms
303  RegisterPlugin(New_ExportCL());
304 
305 #if defined(USE_FFMPEG)
306  RegisterPlugin(New_ExportFFmpeg());
307 #endif
308 }
309 
311 {
312 }
313 
314 void Exporter::SetFileDialogTitle( const wxString & DialogTitle )
315 {
316  // The default title is "Export File"
317  mFileDialogTitle = DialogTitle;
318 }
319 
320 int Exporter::FindFormatIndex(int exportindex)
321 {
322  int c = 0;
323  for (const auto &pPlugin : mPlugins)
324  {
325  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
326  {
327  if (exportindex == c) return j;
328  c++;
329  }
330  }
331  return 0;
332 }
333 
335 {
336  mPlugins.push_back(std::move(ExportPlugin));
337 }
338 
340 {
341  return mPlugins;
342 }
343 
344 bool Exporter::Process(AudacityProject *project, bool selectedOnly, double t0, double t1)
345 {
346  // Save parms
347  mProject = project;
348  mSelectedOnly = selectedOnly;
349  mT0 = t0;
350  mT1 = t1;
351 
352  // Gather track information
353  if (!ExamineTracks()) {
354  return false;
355  }
356 
357  // Ask user for file name
358  if (!GetFilename()) {
359  return false;
360  }
361 
362  // Check for down mixing
363  if (!CheckMix()) {
364  return false;
365  }
366 
367  // Let user edit MetaData
368  if (mPlugins[mFormat]->GetCanMetaData(mSubFormat)) {
369  if (!(project->DoEditMetadata(_("Edit Metadata Tags"), _("Exported Tags"), mProject->GetShowId3Dialog()))) {
370  return false;
371  }
372  }
373 
374  // Ensure filename doesn't interfere with project files.
375  if (!CheckFilename()) {
376  return false;
377  }
378 
379  // Export the tracks
380  bool success = ExportTracks();
381 
382  // Get rid of mixerspec
383  mMixerSpec.reset();
384 
385  return success;
386 }
387 
388 bool Exporter::Process(AudacityProject *project, unsigned numChannels,
389  const wxChar *type, const wxString & filename,
390  bool selectedOnly, double t0, double t1)
391 {
392  // Save parms
393  mProject = project;
394  mChannels = numChannels;
395  mFilename = filename;
396  mSelectedOnly = selectedOnly;
397  mT0 = t0;
398  mT1 = t1;
400 
401  int i = -1;
402  for (const auto &pPlugin : mPlugins) {
403  ++i;
404  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
405  {
406  if (pPlugin->GetFormat(j).IsSameAs(type, false))
407  {
408  mFormat = i;
409  mSubFormat = j;
410  return CheckFilename() && ExportTracks();
411  }
412  }
413  }
414 
415  return false;
416 }
417 
419 {
420  // Init
421  mNumSelected = 0;
422  mNumLeft = 0;
423  mNumRight = 0;
424  mNumMono = 0;
425 
426  // First analyze the selected audio, perform sanity checks, and provide
427  // information as appropriate.
428 
429  // Tally how many are right, left, mono, and make sure at
430  // least one track is selected (if selectedOnly==true)
431 
432  double earliestBegin = mT1;
433  double latestEnd = mT0;
434 
435  const TrackList *tracks = mProject->GetTracks();
436  TrackListConstIterator iter1(tracks);
437  const Track *tr = iter1.First();
438 
439  while (tr) {
440  if (tr->GetKind() == Track::Wave) {
441  auto wt = static_cast<const WaveTrack *>(tr);
442  if ( (tr->GetSelected() || !mSelectedOnly) &&
443  !wt->GetMute() ) { // don't count muted tracks
444  mNumSelected++;
445 
446  if (tr->GetChannel() == Track::LeftChannel) {
447  mNumLeft++;
448  }
449  else if (tr->GetChannel() == Track::RightChannel) {
450  mNumRight++;
451  }
452  else if (tr->GetChannel() == Track::MonoChannel) {
453  // It's a mono channel, but it may be panned
454  float pan = ((WaveTrack*)tr)->GetPan();
455 
456  if (pan == -1.0)
457  mNumLeft++;
458  else if (pan == 1.0)
459  mNumRight++;
460  else if (pan == 0)
461  mNumMono++;
462  else {
463  // Panned partially off-center. Mix as stereo.
464  mNumLeft++;
465  mNumRight++;
466  }
467  }
468 
469  if (tr->GetOffset() < earliestBegin) {
470  earliestBegin = tr->GetOffset();
471  }
472 
473  if (tr->GetEndTime() > latestEnd) {
474  latestEnd = tr->GetEndTime();
475  }
476 
477  }
478  }
479 
480  tr = iter1.Next();
481  }
482 
483  if (mNumSelected == 0) {
484  wxString message;
485  if(mSelectedOnly)
486  message = _("All selected audio is muted.");
487  else
488  message = _("All audio is muted.");
489  AudacityMessageBox(message,
490  _("Unable to export"),
491  wxOK | wxICON_INFORMATION);
492  return false;
493  }
494 
495  if (mT0 < earliestBegin)
496  mT0 = earliestBegin;
497 
498  if (mT1 > latestEnd)
499  mT1 = latestEnd;
500 
501  return true;
502 }
503 
505 {
506  mFormat = -1;
507 
508  wxString maskString;
509  wxString defaultFormat = mFormatName;
510  if( defaultFormat.IsEmpty() )
511  defaultFormat = gPrefs->Read(wxT("/Export/Format"),
512  wxT("WAV"));
513 
514  mFilterIndex = 0;
515 
516  {
517  int i = -1;
518  for (const auto &pPlugin : mPlugins) {
519  ++i;
520  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
521  {
522  maskString += pPlugin->GetMask(j) + wxT("|");
523  if (mPlugins[i]->GetFormat(j) == defaultFormat) {
524  mFormat = i;
525  mSubFormat = j;
526  }
527  if (mFormat == -1) mFilterIndex++;
528  }
529  }
530  }
531  if (mFormat == -1)
532  {
533  mFormat = 0;
534  mFilterIndex = 0;
535  }
536  maskString.RemoveLast();
537  wxString defext = mPlugins[mFormat]->GetExtension(mSubFormat).Lower();
538 
539 //Bug 1304: Set a default path if none was given. For Export.
540  mFilename = FileNames::DefaultToDocumentsFolder(wxT("/Export/Path"));
541  mFilename.SetName(mProject->GetName());
542  if (mFilename.GetName().empty())
543  mFilename.SetName(_("untitled"));
544  while (true) {
545  // Must reset each iteration
546  mBook = NULL;
547 
548  {
549  auto useFileName = mFilename;
550  if (!useFileName.HasExt())
551  useFileName.SetExt(defext);
554  mFilename.GetPath(),
555  useFileName.GetFullName(),
556  maskString,
557  wxFD_SAVE | wxRESIZE_BORDER);
558  mDialog = &fd;
559  mDialog->PushEventHandler(this);
560 
561  fd.SetUserPaneCreator(CreateUserPaneCallback, (wxUIntPtr) this);
562  fd.SetFilterIndex(mFilterIndex);
563 
564  int result = fd.ShowModal();
565 
566  mDialog->PopEventHandler();
567 
568  if (result == wxID_CANCEL) {
569  return false;
570  }
571 
572  mFilename = fd.GetPath();
573  if (mFilename == wxT("")) {
574  return false;
575  }
576 
577  mFormat = fd.GetFilterIndex();
578  mFilterIndex = fd.GetFilterIndex();
579  }
580 
581  int c = 0;
582  int i = -1;
583  for (const auto &pPlugin : mPlugins)
584  {
585  ++i;
586  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
587  {
588  if (mFilterIndex == c)
589  {
590  mFormat = i;
591  mSubFormat = j;
592  }
593  c++;
594  }
595  }
596 
597  wxString ext = mFilename.GetExt();
598  defext = mPlugins[mFormat]->GetExtension(mSubFormat).Lower();
599 
600  //
601  // Check the extension - add the default if it's not there,
602  // and warn user if it's abnormal.
603  //
604  if (ext.IsEmpty()) {
605  //
606  // Make sure the user doesn't accidentally save the file
607  // as an extension with no name, like just plain ".wav".
608  //
609  if (mFilename.GetName().Left(1) == wxT(".")) {
610  wxString prompt = wxString::Format(
611  _("Are you sure you want to export the file as \"%s\"?\n"),
612  mFilename.GetFullName() );
613 
614  int action = AudacityMessageBox(prompt,
615  _("Warning"),
616  wxYES_NO | wxICON_EXCLAMATION);
617  if (action != wxYES) {
618  continue;
619  }
620  }
621 
622  mFilename.SetExt(defext);
623  }
624  else if (!mPlugins[mFormat]->CheckFileName(mFilename, mSubFormat))
625  {
626  continue;
627  }
628  else if (!ext.IsEmpty() && !mPlugins[mFormat]->IsExtension(ext,mSubFormat) && ext.CmpNoCase(defext)) {
629  wxString prompt;
630  prompt.Printf(_("You are about to export a %s file with the name \"%s\".\n\nNormally these files end in \".%s\", and some programs will not open files with nonstandard extensions.\n\nAre you sure you want to export the file under this name?"),
631  mPlugins[mFormat]->GetFormat(mSubFormat),
632  mFilename.GetFullName(),
633  defext);
634 
635  int action = AudacityMessageBox(prompt,
636  _("Warning"),
637  wxYES_NO | wxICON_EXCLAMATION);
638  if (action != wxYES) {
639  continue;
640  }
641  }
642 
643  if (mFilename.GetFullPath().Length() >= 256) {
644  AudacityMessageBox(_("Sorry, pathnames longer than 256 characters not supported."));
645  continue;
646  }
647 
648  // Check to see if we are writing to a path that a missing aliased file existed at.
649  // This causes problems for the exporter, so we don't allow it.
650  // Overwritting non-missing aliased files is okay.
651  // Also, this can only happen for uncompressed audio.
652  bool overwritingMissingAlias;
653  overwritingMissingAlias = false;
654  for (size_t i = 0; i < gAudacityProjects.size(); i++) {
655  AliasedFileArray aliasedFiles;
656  FindDependencies(gAudacityProjects[i].get(), aliasedFiles);
657  for (const auto &aliasedFile : aliasedFiles) {
658  if (mFilename.GetFullPath() == aliasedFile.mFileName.GetFullPath() &&
659  !mFilename.FileExists()) {
660  // Warn and return to the dialog
661  AudacityMessageBox(_("You are attempting to overwrite an aliased file that is missing.\n\
662  The file cannot be written because the path is needed to restore the original audio to the project.\n\
663  Choose Help > Diagnostics > Check Dependencies to view the locations of all missing files.\n\
664  If you still wish to export, please choose a different filename or folder."));
665  overwritingMissingAlias = true;
666  }
667  }
668  }
669  if (overwritingMissingAlias)
670  continue;
671 
672  if (mFilename.FileExists()) {
673  wxString prompt;
674 
675  prompt.Printf(_("A file named \"%s\" already exists. Replace?"),
676  mFilename.GetFullPath());
677 
678  int action = AudacityMessageBox(prompt,
679  _("Warning"),
680  wxYES_NO | wxICON_EXCLAMATION);
681  if (action != wxYES) {
682  continue;
683  }
684  }
685 
686  break;
687  }
688 
689  return true;
690 }
691 
692 //
693 // For safety, if the file already exists it stores the filename
694 // the user wants in actualName, and returns a temporary file name.
695 // The calling function should rename the file when it's successfully
696 // exported.
697 //
699 {
700  //
701  // Ensure that exporting a file by this name doesn't overwrite
702  // one of the existing files in the project. (If it would
703  // overwrite an existing file, DirManager tries to rename the
704  // existing file.)
705  //
706 
707  if (!mProject->GetDirManager()->EnsureSafeFilename(mFilename))
708  return false;
709 
710  if( mFormatName.IsEmpty() )
711  gPrefs->Write(wxT("/Export/Format"), mPlugins[mFormat]->GetFormat(mSubFormat));
712  gPrefs->Write(wxT("/Export/Path"), mFilename.GetPath());
713  gPrefs->Flush();
714 
715  //
716  // To be even safer, return a temporary file name based
717  // on this one...
718  //
719 
721 
722  int suffix = 0;
723  while (mFilename.FileExists()) {
724  mFilename.SetName(mActualName.GetName() +
725  wxString::Format(wxT("%d"), suffix));
726  suffix++;
727  }
728 
729  return true;
730 }
731 
733 {
734  int c = 0;
735  int mf = -1, msf = -1;
736  int i = -1;
737  for (const auto &pPlugin : mPlugins)
738  {
739  ++i;
740  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
741  {
742  if (index == c)
743  {
744  mf = i;
745  msf = j;
746  }
747  c++;
748  }
749  }
750  // This shouldn't happen...
751  if (index >= c) {
752  return;
753  }
754 
755 #if defined(__WXMSW__)
756  mPlugins[mf]->DisplayOptions(mProject, msf);
757 #else
758  mPlugins[mf]->DisplayOptions(mDialog, msf);
759 #endif
760 }
761 
763 {
764  // Clean up ... should never happen
765  mMixerSpec.reset();
766 
767  // Detemine if exported file will be stereo or mono or multichannel,
768  // and if mixing will occur.
769 
770  int downMix = gPrefs->Read(wxT("/FileFormats/ExportDownMix"), true);
771  int exportedChannels = mPlugins[mFormat]->SetNumExportChannels();
772 
773  if (downMix) {
774  if (mNumRight > 0 || mNumLeft > 0) {
775  mChannels = 2;
776  }
777  else {
778  mChannels = 1;
779  }
781  mPlugins[mFormat]->GetMaxChannels(mSubFormat));
782 
783  auto numLeft = mNumLeft + mNumMono;
784  auto numRight = mNumRight + mNumMono;
785 
786  if (numLeft > 1 || numRight > 1 || mNumLeft + mNumRight + mNumMono > mChannels) {
787  wxString exportFormat = mPlugins[mFormat]->GetFormat(mSubFormat);
788  if (exportFormat != wxT("CL") && exportFormat != wxT("FFMPEG") && exportedChannels == -1)
789  exportedChannels = mChannels;
790 
791  if (exportedChannels == 1) {
793  wxT("MixMono"),
794  _("Your tracks will be mixed down to a single mono channel in the exported file."),
795  true) == wxID_CANCEL)
796  return false;
797  }
798  else if (exportedChannels == 2) {
800  wxT("MixStereo"),
801  _("Your tracks will be mixed down to two stereo channels in the exported file."),
802  true) == wxID_CANCEL)
803  return false;
804  }
805  else {
807  wxT("MixUnknownChannels"),
808  _("Your tracks will be mixed down to one exported file according to the encoder settings."),
809  true) == wxID_CANCEL)
810  return false;
811  }
812  }
813  }
814  else
815  {
816  if (exportedChannels < 0)
817  exportedChannels = mPlugins[mFormat]->GetMaxChannels(mSubFormat);
818 
821  exportedChannels,
822  NULL,
823  1,
824  _("Advanced Mixing Options"));
825 
826  if (md.ShowModal() != wxID_OK) {
827  return false;
828  }
829 
830  mMixerSpec = std::make_unique<MixerSpec>(*(md.GetMixerSpec()));
831  mChannels = mMixerSpec->GetNumChannels();
832  }
833 
834  return true;
835 }
836 
838 {
839  // Keep original in case of failure
840  if (mActualName != mFilename) {
841  ::wxRenameFile(mActualName.GetFullPath(), mFilename.GetFullPath());
842  }
843 
844  bool success = false;
845 
846  auto cleanup = finally( [&] {
847  if (mActualName != mFilename) {
848  // Remove backup
849  if ( success )
850  ::wxRemoveFile(mFilename.GetFullPath());
851  else {
852  // Restore original, if needed
853  ::wxRemoveFile(mActualName.GetFullPath());
854  ::wxRenameFile(mFilename.GetFullPath(), mActualName.GetFullPath());
855  }
856  }
857  else {
858  if ( ! success )
859  // Remove any new, and only partially written, file.
860  ::wxRemoveFile(mFilename.GetFullPath());
861  }
862  } );
863 
864  std::unique_ptr<ProgressDialog> pDialog;
865  auto result = mPlugins[mFormat]->Export(mProject,
866  pDialog,
867  mChannels,
868  mActualName.GetFullPath(),
870  mT0,
871  mT1,
872  mMixerSpec.get(),
873  NULL,
874  mSubFormat);
875 
876  success =
877  result == ProgressResult::Success || result == ProgressResult::Stopped;
878 
879  return success;
880 }
881 
882 void Exporter::CreateUserPaneCallback(wxWindow *parent, wxUIntPtr userdata)
883 {
884  Exporter *self = (Exporter *) userdata;
885  if (self)
886  {
887  self->CreateUserPane(parent);
888  }
889 }
890 
891 void Exporter::CreateUserPane(wxWindow *parent)
892 {
893  ShuttleGui S(parent, eIsCreating);
894 
895  S.StartVerticalLay();
896  {
897  S.StartHorizontalLay(wxEXPAND);
898  {
899  S.StartStatic(_("Format Options"), 1);
900  {
901  mBook = safenew wxSimplebook(S.GetParent());
902  S.AddWindow(mBook, wxEXPAND);
903 
904  for (const auto &pPlugin : mPlugins)
905  {
906  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
907  {
908  mBook->AddPage(pPlugin->OptionsCreate(mBook, j), wxEmptyString);
909  }
910  }
911  }
912  S.EndStatic();
913  }
914  S.EndHorizontalLay();
915  }
916  S.EndVerticalLay();
917 
918  return;
919 }
920 
921 void Exporter::OnFilterChanged(wxFileCtrlEvent & evt)
922 {
923  int index = evt.GetFilterIndex();
924 
925  // On GTK, this event can fire before the userpane is created
926  if (mBook == NULL || index < 0 || index >= (int) mBook->GetPageCount())
927  {
928  return;
929  }
930 
931  mBook->ChangeSelection(index);
932 }
933 
935  bool selectedOnly,
936  double t0,
937  double t1,
938  wxFileName fnFile,
939  int iFormat,
940  int iSubFormat,
941  int iFilterIndex)
942 {
943  // Save parms
944  mProject = project;
945  mSelectedOnly = selectedOnly;
946  mT0 = t0;
947  mT1 = t1;
948 
949  // Auto Export Parameters
950  mFilename = fnFile;
951  mFormat = iFormat;
952  mSubFormat = iSubFormat;
953  mFilterIndex = iFilterIndex;
954 
955  // Gather track information
956  if (!ExamineTracks()) {
957  return false;
958  }
959 
960  // Check for down mixing
961  if (!CheckMix()) {
962  return false;
963  }
964 
965  // Ensure filename doesn't interfere with project files.
966  if (!CheckFilename()) {
967  return false;
968  }
969 
970  // Export the tracks
971  bool success = ExportTracks();
972 
973  // Get rid of mixerspec
974  mMixerSpec.reset();
975 
976  return success;
977 }
978 
980  return mFormat;
981 }
982 
984  return mSubFormat;
985 }
986 
988  return mFormat;
989 }
990 
992  return mFilename;
993 }
994 
996  mFormat = -1;
997  mProject = project;
998 
999  if( GetFilename()==false )
1000  return false;
1001 
1002  // Let user edit MetaData
1003  if (mPlugins[mFormat]->GetCanMetaData(mSubFormat)) {
1004  if (!(project->DoEditMetadata(_("Edit Metadata Tags"),
1005  _("Exported Tags"), mProject->GetShowId3Dialog()))) {
1006  return false;
1007  }
1008  }
1009 
1010  return true;
1011 }
1012 
1013 //----------------------------------------------------------------------------
1014 // ExportMixerPanel
1015 //----------------------------------------------------------------------------
1016 
1017 BEGIN_EVENT_TABLE(ExportMixerPanel, wxPanelWrapper)
1018  EVT_PAINT(ExportMixerPanel::OnPaint)
1019  EVT_MOUSE_EVENTS(ExportMixerPanel::OnMouseEvent)
1021 
1023  wxArrayString trackNames,wxWindow *parent, wxWindowID id,
1024  const wxPoint& pos, const wxSize& size):
1025  wxPanelWrapper(parent, id, pos, size)
1026  , mMixerSpec{mixerSpec}
1027  , mChannelRects{ mMixerSpec->GetMaxNumChannels() }
1028  , mTrackRects{ mMixerSpec->GetNumTracks() }
1029 {
1030  mBitmap = NULL;
1031  mWidth = 0;
1032  mHeight = 0;
1033  mSelectedTrack = mSelectedChannel = -1;
1034 
1035  mTrackNames = trackNames;
1036 }
1037 
1039 {
1040 }
1041 
1042 //set the font on memDC such that text can fit in specified width and height
1043 void ExportMixerPanel::SetFont(wxMemoryDC &memDC, const wxString &text, int width,
1044  int height )
1045 {
1046  int l = 0, u = 13, m, w, h;
1047  wxFont font = memDC.GetFont();
1048  while( l < u - 1 )
1049  {
1050  m = ( l + u ) / 2;
1051  font.SetPointSize( m );
1052  memDC.SetFont( font );
1053  memDC.GetTextExtent( text, &w, &h );
1054 
1055  if( w < width && h < height )
1056  l = m;
1057  else
1058  u = m;
1059  }
1060  font.SetPointSize( l );
1061  memDC.SetFont( font );
1062 }
1063 
1064 void ExportMixerPanel::OnPaint(wxPaintEvent & WXUNUSED(event))
1065 {
1066  wxPaintDC dc( this );
1067 
1068  int width, height;
1069  GetSize( &width, &height );
1070 
1071  if( !mBitmap || mWidth != width || mHeight != height )
1072  {
1073  mWidth = width;
1074  mHeight = height;
1075  mBitmap = std::make_unique<wxBitmap>( mWidth, mHeight );
1076  }
1077 
1078  wxColour bkgnd = GetBackgroundColour();
1079  wxBrush bkgndBrush( bkgnd, wxSOLID );
1080 
1081  wxMemoryDC memDC;
1082  memDC.SelectObject( *mBitmap );
1083 
1084  //draw background
1085  wxRect bkgndRect;
1086  bkgndRect.x = 0;
1087  bkgndRect.y = 0;
1088  bkgndRect.width = mWidth;
1089  bkgndRect.height = mHeight;
1090 
1091  memDC.SetBrush( *wxWHITE_BRUSH );
1092  memDC.SetPen( *wxBLACK_PEN );
1093  memDC.DrawRectangle( bkgndRect );
1094 
1095  //box dimensions
1096  mBoxWidth = mWidth / 6;
1097 
1098  mTrackHeight = ( mHeight * 3 ) / ( mMixerSpec->GetNumTracks() * 4 );
1099  if( mTrackHeight > 30 )
1100  mTrackHeight = 30;
1101 
1102  mChannelHeight = ( mHeight * 3 ) / ( mMixerSpec->GetNumChannels() * 4 );
1103  if( mChannelHeight > 30 )
1104  mChannelHeight = 30;
1105 
1106  static double PI = 2 * acos( 0.0 );
1107  double angle = atan( ( 3.0 * mHeight ) / mWidth );
1108  double radius = mHeight / ( 2.0 * sin( PI - 2.0 * angle ) );
1109  double totAngle = ( asin( mHeight / ( 2.0 * radius ) ) * 2.0 );
1110 
1111  //draw tracks
1112  memDC.SetBrush( AColor::envelopeBrush );
1113  angle = totAngle / ( mMixerSpec->GetNumTracks() + 1 );
1114 
1115  int max = 0, w, h;
1116  for( unsigned int i = 1; i < mMixerSpec->GetNumTracks(); i++ )
1117  if( mTrackNames[ i ].length() > mTrackNames[ max ].length() )
1118  max = i;
1119 
1120  SetFont( memDC, mTrackNames[ max ], mBoxWidth, mTrackHeight );
1121 
1122  for( unsigned int i = 0; i < mMixerSpec->GetNumTracks(); i++ )
1123  {
1124  mTrackRects[ i ].x = (int)( mBoxWidth * 2 + radius - radius *
1125  cos( totAngle / 2.0 - angle * ( i + 1 ) ) - mBoxWidth + 0.5 );
1126  mTrackRects[ i ].y = (int)( mHeight * 0.5 - radius *
1127  sin( totAngle * 0.5 - angle * ( i + 1.0 ) ) -
1128  0.5 * mTrackHeight + 0.5 );
1129 
1130  mTrackRects[ i ].width = mBoxWidth;
1131  mTrackRects[ i ].height = mTrackHeight;
1132 
1133  memDC.SetPen( mSelectedTrack == (int)i ? *wxRED_PEN : *wxBLACK_PEN );
1134  memDC.DrawRectangle( mTrackRects[ i ] );
1135 
1136  memDC.GetTextExtent( mTrackNames[ i ], &w, &h );
1137  memDC.DrawText( mTrackNames[ i ],
1138  mTrackRects[ i ].x + ( mBoxWidth - w ) / 2,
1139  mTrackRects[ i ].y + ( mTrackHeight - h ) / 2 );
1140  }
1141 
1142  //draw channels
1143  memDC.SetBrush( AColor::playRegionBrush[ 0 ] );
1144  angle = ( asin( mHeight / ( 2.0 * radius ) ) * 2.0 ) /
1145  ( mMixerSpec->GetNumChannels() + 1 );
1146 
1147  SetFont( memDC, wxT( "Channel: XX" ), mBoxWidth, mChannelHeight );
1148  memDC.GetTextExtent( wxT( "Channel: XX" ), &w, &h );
1149 
1150  for( unsigned int i = 0; i < mMixerSpec->GetNumChannels(); i++ )
1151  {
1152  mChannelRects[ i ].x = (int)( mBoxWidth * 4 - radius + radius *
1153  cos( totAngle * 0.5 - angle * ( i + 1 ) ) + 0.5 );
1154  mChannelRects[ i ].y = (int)( mHeight * 0.5 - radius *
1155  sin( totAngle * 0.5 - angle * ( i + 1 ) ) -
1156  0.5 * mChannelHeight + 0.5 );
1157 
1158  mChannelRects[ i ].width = mBoxWidth;
1159  mChannelRects[ i ].height = mChannelHeight;
1160 
1161  memDC.SetPen( mSelectedChannel == (int)i ? *wxRED_PEN : *wxBLACK_PEN );
1162  memDC.DrawRectangle( mChannelRects[ i ] );
1163 
1164  memDC.DrawText( wxString::Format( _( "Channel: %2d" ), i + 1 ),
1165  mChannelRects[ i ].x + ( mBoxWidth - w ) / 2,
1166  mChannelRects[ i ].y + ( mChannelHeight - h ) / 2 );
1167  }
1168 
1169  //draw links
1170  memDC.SetPen( wxPen( *wxBLACK, mHeight / 200 ) );
1171  for( unsigned int i = 0; i < mMixerSpec->GetNumTracks(); i++ )
1172  for( unsigned int j = 0; j < mMixerSpec->GetNumChannels(); j++ )
1173  if( mMixerSpec->mMap[ i ][ j ] )
1174  AColor::Line(memDC, mTrackRects[ i ].x + mBoxWidth,
1175  mTrackRects[ i ].y + mTrackHeight / 2, mChannelRects[ j ].x,
1176  mChannelRects[ j ].y + mChannelHeight / 2 );
1177 
1178  dc.Blit( 0, 0, mWidth, mHeight, &memDC, 0, 0, wxCOPY, FALSE );
1179 }
1180 
1181 double ExportMixerPanel::Distance( wxPoint &a, wxPoint &b )
1182 {
1183  return sqrt( pow( a.x - b.x, 2.0 ) + pow( a.y - b.y, 2.0 ) );
1184 }
1185 
1186 //checks if p is on the line connecting la, lb with tolerence
1187 bool ExportMixerPanel::IsOnLine( wxPoint p, wxPoint la, wxPoint lb )
1188 {
1189  return Distance( p, la ) + Distance( p, lb ) - Distance( la, lb ) < 0.1;
1190 }
1191 
1192 void ExportMixerPanel::OnMouseEvent(wxMouseEvent & event)
1193 {
1194  if( event.ButtonDown() )
1195  {
1196  bool reset = true;
1197  //check tracks
1198  for( unsigned int i = 0; i < mMixerSpec->GetNumTracks(); i++ )
1199  if( mTrackRects[ i ].Contains( event.m_x, event.m_y ) )
1200  {
1201  reset = false;
1202  if( mSelectedTrack == (int)i )
1203  mSelectedTrack = -1;
1204  else
1205  {
1206  mSelectedTrack = i;
1207  if( mSelectedChannel != -1 )
1210  }
1211  goto found;
1212  }
1213 
1214  //check channels
1215  for( unsigned int i = 0; i < mMixerSpec->GetNumChannels(); i++ )
1216  if( mChannelRects[ i ].Contains( event.m_x, event.m_y ) )
1217  {
1218  reset = false;
1219  if( mSelectedChannel == (int)i )
1220  mSelectedChannel = -1;
1221  else
1222  {
1223  mSelectedChannel = i;
1224  if( mSelectedTrack != -1 )
1227  }
1228  goto found;
1229  }
1230 
1231  //check links
1232  for( unsigned int i = 0; i < mMixerSpec->GetNumTracks(); i++ )
1233  for( unsigned int j = 0; j < mMixerSpec->GetNumChannels(); j++ )
1234  if( mMixerSpec->mMap[ i ][ j ] && IsOnLine( wxPoint( event.m_x,
1235  event.m_y ), wxPoint( mTrackRects[ i ].x + mBoxWidth,
1236  mTrackRects[ i ].y + mTrackHeight / 2 ),
1237  wxPoint( mChannelRects[ j ].x, mChannelRects[ j ].y +
1238  mChannelHeight / 2 ) ) )
1239  mMixerSpec->mMap[ i ][ j ] = false;
1240 
1241 found:
1242  if( reset )
1244  Refresh( false );
1245  }
1246 }
1247 
1248 //----------------------------------------------------------------------------
1249 // ExportMixerDialog
1250 //----------------------------------------------------------------------------
1251 
1252 enum
1253 {
1254  ID_MIXERPANEL = 10001,
1256 };
1257 
1258 BEGIN_EVENT_TABLE( ExportMixerDialog, wxDialogWrapper )
1259  EVT_BUTTON( wxID_OK, ExportMixerDialog::OnOk )
1260  EVT_BUTTON( wxID_CANCEL, ExportMixerDialog::OnCancel )
1261  EVT_SIZE( ExportMixerDialog::OnSize )
1262  EVT_SLIDER( ID_SLIDER_CHANNEL, ExportMixerDialog::OnSlider )
1264 
1265 ExportMixerDialog::ExportMixerDialog( const TrackList *tracks, bool selectedOnly,
1266  unsigned maxNumChannels, wxWindow *parent, wxWindowID id, const wxString &title,
1267  const wxPoint &position, const wxSize& size, long style ) :
1268  wxDialogWrapper( parent, id, title, position, size, style | wxRESIZE_BORDER )
1269 {
1270  SetName(GetTitle());
1271 
1272  unsigned numTracks = 0;
1273  TrackListConstIterator iter( tracks );
1274 
1275  for( const Track *t = iter.First(); t; t = iter.Next() )
1276  {
1277  auto wt = static_cast<const WaveTrack *>(t);
1278  if( t->GetKind() == Track::Wave && ( t->GetSelected() || !selectedOnly ) &&
1279  !wt->GetMute() )
1280  {
1281  numTracks++;
1282  const wxString sTrackName = (t->GetName()).Left(20);
1283  if( t->GetChannel() == Track::LeftChannel )
1284  /* i18n-hint: track name and L abbreviating Left channel */
1285  mTrackNames.Add( wxString::Format( _( "%s - L" ), sTrackName ) );
1286  else if( t->GetChannel() == Track::RightChannel )
1287  /* i18n-hint: track name and R abbreviating Right channel */
1288  mTrackNames.Add( wxString::Format( _( "%s - R" ), sTrackName ) );
1289  else
1290  mTrackNames.Add(sTrackName);
1291  }
1292  }
1293 
1294  // JKC: This is an attempt to fix a 'watching brief' issue, where the slider is
1295  // sometimes not slidable. My suspicion is that a mixer may incorrectly
1296  // state the number of channels - so we assume there are always at least two.
1297  // The downside is that if someone is exporting to a mono device, the dialog
1298  // will allow them to output to two channels. Hmm. We may need to revisit this.
1299 
1300  if (maxNumChannels < 2 )
1301  // STF (April 2016): AMR (narrowband) and MP3 may export 1 channel.
1302  // maxNumChannels = 2;
1303  maxNumChannels = 1;
1304  if (maxNumChannels > 32)
1305  maxNumChannels = 32;
1306 
1307  mMixerSpec = std::make_unique<MixerSpec>(numTracks, maxNumChannels);
1308 
1309  wxBoxSizer *vertSizer;
1310  {
1311  auto uVertSizer = std::make_unique<wxBoxSizer>(wxVERTICAL);
1312  vertSizer = uVertSizer.get();
1313 
1314  wxWindow *mixerPanel = safenew ExportMixerPanel(mMixerSpec.get(), mTrackNames, this,
1315  ID_MIXERPANEL, wxDefaultPosition, wxSize(400, -1));
1316  mixerPanel->SetName(_("Mixer Panel"));
1317  vertSizer->Add(mixerPanel, 1, wxEXPAND | wxALIGN_CENTRE | wxALL, 5);
1318 
1319  {
1320  auto horSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
1321 
1322  wxString label;
1323  label.Printf(_("Output Channels: %2d"), mMixerSpec->GetNumChannels());
1324  mChannelsText = safenew wxStaticText(this, -1, label);
1325  horSizer->Add(mChannelsText, 0, wxALIGN_LEFT | wxALL, 5);
1326 
1327  wxSlider *channels = safenew wxSlider(this, ID_SLIDER_CHANNEL,
1328  mMixerSpec->GetNumChannels(), 1, mMixerSpec->GetMaxNumChannels(),
1329  wxDefaultPosition, wxSize(300, -1));
1330  channels->SetName(label);
1331  horSizer->Add(channels, 0, wxEXPAND | wxALL, 5);
1332 
1333  vertSizer->Add(horSizer.release(), 0, wxALIGN_CENTRE | wxALL, 5);
1334  }
1335 
1336  vertSizer->Add(CreateStdButtonSizer(this, eCancelButton | eOkButton).release(), 0, wxEXPAND);
1337 
1338  SetAutoLayout(true);
1339  SetSizer(uVertSizer.release());
1340  }
1341 
1342  vertSizer->Fit( this );
1343  vertSizer->SetSizeHints( this );
1344 
1345  SetSizeHints( 640, 480, 20000, 20000 );
1346 
1347  SetSize( 640, 480 );
1348  Center();
1349 }
1350 
1352 {
1353 }
1354 
1355 void ExportMixerDialog::OnSize(wxSizeEvent &event)
1356 {
1357  ExportMixerPanel *pnl = ( ( ExportMixerPanel* ) FindWindow( ID_MIXERPANEL ) );
1358  pnl->Refresh( false );
1359  event.Skip();
1360 }
1361 
1362 void ExportMixerDialog::OnSlider( wxCommandEvent & WXUNUSED(event))
1363 {
1364  wxSlider *channels = ( wxSlider* )FindWindow( ID_SLIDER_CHANNEL );
1365  ExportMixerPanel *pnl = ( ( ExportMixerPanel* ) FindWindow( ID_MIXERPANEL ) );
1366  mMixerSpec->SetNumChannels( channels->GetValue() );
1367  pnl->Refresh( false );
1368  wxString label;
1369  label.Printf( _( "Output Channels: %2d" ), mMixerSpec->GetNumChannels() );
1370  mChannelsText->SetLabel( label );
1371  channels->SetName( label );
1372 }
1373 
1374 void ExportMixerDialog::OnOk(wxCommandEvent & WXUNUSED(event))
1375 {
1376  EndModal( wxID_OK );
1377 }
1378 
1379 void ExportMixerDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
1380 {
1381  EndModal( wxID_CANCEL );
1382 }
1383 
virtual bool CheckFileName(wxFileName &filename, int format=0)
Definition: Export.cpp:91
#define PI
Definition: ScienFilter.cpp:62
std::unique_ptr< wxSizer > CreateStdButtonSizer(wxWindow *parent, long buttons, wxWindow *extra)
A list of TrackListNode items.
Definition: Track.h:553
wxFileName mFilename
Definition: Export.h:210
virtual ~ExportMixerDialog()
Definition: Export.cpp:1351
static const wxChar * exts[]
Definition: ImportFLAC.cpp:52
virtual bool IsExtension(const wxString &ext, int index)
Definition: Export.cpp:202
std::unique_ptr< MixerSpec > mMixerSpec
Definition: Export.h:281
wxFileName GetAutoExportFileName()
Definition: Export.cpp:991
void RegisterPlugin(movable_ptr< ExportPlugin > &&plugin)
Definition: Export.cpp:334
virtual wxString GetDescription(int index)
Definition: Export.cpp:159
bool SetAutoExportOptions(AudacityProject *project)
Definition: Export.cpp:995
void FindDependencies(AudacityProject *project, AliasedFileArray &outAliasedFiles)
std::unique_ptr< T > movable_ptr
Definition: MemoryX.h:713
virtual double GetOffset() const =0
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
void OnFilterChanged(wxFileCtrlEvent &evt)
Definition: Export.cpp:921
movable_ptr< ExportPlugin > New_ExportMP3()
Definition: ExportMP3.cpp:2167
wxStaticText * mChannelsText
Definition: Export.h:280
bool GetSelected() const
Definition: Track.h:217
wxWindow * AddWindow(wxWindow *pWindow, int Flags=wxALIGN_CENTRE|wxALL)
Definition: ShuttleGui.cpp:257
static wxFileNameWrapper DefaultToDocumentsFolder(const wxString &preference)
Definition: FileNames.cpp:348
movable_ptr< ExportPlugin > New_ExportPCM()
Definition: ExportPCM.cpp:912
void SetFileDialogTitle(const wxString &DialogTitle)
Definition: Export.cpp:314
const Track * First(const TrackList *val=NULL)
Definition: Track.h:402
virtual bool DisplayOptions(wxWindow *parent, int format=0)
Definition: Export.cpp:216
double mT1
Definition: Export.h:214
AProjectArray gAudacityProjects
Definition: Project.cpp:297
int mSelectedChannel
Definition: Export.h:252
void OnSize(wxSizeEvent &event)
Definition: Export.cpp:1355
virtual double GetEndTime() const =0
void SetDescription(const wxString &description, int index)
Definition: Export.cpp:124
ArrayOf< wxRect > mTrackRects
Definition: Export.h:251
bool mSelectedOnly
Definition: Export.h:223
movable_ptr< ExportPlugin > New_ExportMP2()
wxString label
Definition: Tags.cpp:727
bool Process(AudacityProject *project, bool selectedOnly, double t0, double t1)
Definition: Export.cpp:344
movable_ptr< ExportPlugin > New_ExportOGG()
int mSelectedTrack
Definition: Export.h:252
virtual int GetChannel() const
Definition: Track.h:223
movable_ptr< ExportPlugin > New_ExportFFmpeg()
wxString mFormatName
Definition: Export.h:202
virtual wxWindow * OptionsCreate(wxWindow *parent, int format)=0
Definition: Export.cpp:221
void OnCancel(wxCommandEvent &event)
Definition: Export.cpp:1379
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
std::unique_ptr< MixerSpec > mMixerSpec
Definition: Export.h:206
virtual wxString GetFormat(int index)
Definition: Export.cpp:154
void SetFont(wxMemoryDC &memDC, const wxString &text, int width, int height)
Definition: Export.cpp:1043
virtual bool GetCanMetaData(int index)
Definition: Export.cpp:197
unsigned mChannels
Definition: Export.h:222
bool CheckMix()
Definition: Export.cpp:762
int GetAutoExportFormat()
Definition: Export.cpp:979
bool GetFilename()
Definition: Export.cpp:504
bool IsOnLine(wxPoint p, wxPoint la, wxPoint lb)
Definition: Export.cpp:1187
std::unique_ptr< Mixer > CreateMixer(const WaveTrackConstArray &inputTracks, const TimeTrack *timeTrack, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, bool highQuality=true, MixerSpec *mixerSpec=NULL)
Definition: Export.cpp:241
void OnMouseEvent(wxMouseEvent &event)
Definition: Export.cpp:1192
unsigned mNumRight
Definition: Export.h:220
void SetFormat(const wxString &format, int index)
Definition: Export.cpp:119
AudacityProject * mProject
Definition: Export.h:205
#define safenew
Definition: Audacity.h:223
virtual int GetKind() const
Definition: Track.h:267
int FindFormatIndex(int exportindex)
Definition: Export.cpp:320
void EndHorizontalLay()
Definition: ShuttleGui.cpp:975
void SetExtensions(const wxArrayString &extensions, int index)
Definition: Export.cpp:134
const Track * Next(bool skiplinked=false)
Definition: Track.h:406
int ShowWarningDialog(wxWindow *parent, const wxString &internalDialogName, const wxString &message, bool showCancelButton, const wxString &footer)
Definition: Warning.cpp:92
const ExportPluginArray & GetPlugins()
Definition: Export.cpp:339
const std::shared_ptr< DirManager > & GetDirManager()
Definition: Project.cpp:1407
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:161
void EndVerticalLay()
Definition: ShuttleGui.cpp:991
A kind of Track used to 'warp time'.
Definition: TimeTrack.h:29
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
virtual unsigned GetMaxChannels(int index)
Definition: Export.cpp:192
wxString mFileDialogTitle
Definition: Export.h:204
int format
Definition: ExportPCM.cpp:56
virtual ~ExportPlugin()
Definition: Export.cpp:86
unsigned GetNumTracks()
Definition: Mix.h:75
static void CreateUserPaneCallback(wxWindow *parent, wxUIntPtr userdata)
Definition: Export.cpp:882
movable_ptr< ExportPlugin > New_ExportCL()
Definition: ExportCL.cpp:548
bool CheckFilename()
Definition: Export.cpp:698
virtual int GetFormatCount()
Definition: Export.cpp:110
wxWindow * GetParent()
Definition: ShuttleGui.h:259
void SetMaxChannels(unsigned maxchannels, unsigned index)
Definition: Export.cpp:144
bool ProcessFromTimerRecording(AudacityProject *project, bool selectedOnly, double t0, double t1, wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex)
Definition: Export.cpp:934
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:966
ExportPlugin()
Definition: Export.cpp:81
ArraysOf< bool > mMap
Definition: Mix.h:65
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
Definition: AudioIO.h:65
wxArrayString mTrackNames
Definition: Export.h:253
unsigned mNumLeft
Definition: Export.h:219
bool GetShowId3Dialog()
Definition: Project.h:301
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
void DisplayOptions(int index)
Definition: Export.cpp:732
int mSubFormat
Definition: Export.h:217
Fundamental data object of Audacity, placed in the TrackPanel. Classes derived form it include the Wa...
Definition: Track.h:67
std::list< AliasedFile > AliasedFileArray
Definition: Dependencies.h:53
ExportPluginArray mPlugins
Definition: Export.h:208
WX_DEFINE_USER_EXPORTED_OBJARRAY(FormatInfoArray)
FileDialogWrapper * mDialog
Definition: Export.h:203
int min(int a, int b)
bool ExportTracks()
Definition: Export.cpp:837
virtual ~ExportMixerPanel()
Definition: Export.cpp:1038
wxSimplebook * mBook
Definition: Export.h:225
void AddTitle(const wxString &Prompt)
Centred text string.
Definition: ShuttleGui.cpp:243
int GetAutoExportFilterIndex()
Definition: Export.cpp:987
double Distance(wxPoint &a, wxPoint &b)
Definition: Export.cpp:1181
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:122
virtual wxString GetExtension(int index=0)
Return the (first) file name extension for the sub-format.
Definition: Export.cpp:164
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
static wxBrush envelopeBrush
Definition: AColor.h:124
bool DoEditMetadata(const wxString &title, const wxString &shortUndoDescription, bool force)
Definition: Menus.cpp:7005
std::unique_ptr< wxBitmap > mBitmap
Definition: Export.h:245
wxFileName mActualName
Definition: Export.h:211
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
unsigned mNumMono
Definition: Export.h:221
unsigned GetNumChannels()
Definition: Mix.h:72
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
Definition: Export.cpp:103
void OnPaint(wxPaintEvent &event)
Definition: Export.cpp:1064
movable_ptr< ExportPlugin > New_ExportFLAC()
void OnSlider(wxCommandEvent &event)
Definition: Export.cpp:1362
void SetCanMetaData(bool canmetadata, int index)
Definition: Export.cpp:149
ArrayOf< wxRect > mChannelRects
Definition: Export.h:250
wxStaticBox * StartStatic(const wxString &Str, int iProp=0)
Definition: ShuttleGui.cpp:701
void AddExtension(const wxString &extension, int index)
Definition: Export.cpp:129
double mT0
Definition: Export.h:213
virtual wxString GetMask(int index)
Definition: Export.cpp:174
wxString GetName()
Definition: Project.cpp:1443
virtual wxArrayString GetExtensions(int index=0)
Return all the file name extensions used for the sub-format.
Definition: Export.cpp:169
int mFormat
Definition: Export.h:216
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:374
void CreateUserPane(wxWindow *parent)
Definition: Export.cpp:891
bool ExamineTracks()
Definition: Export.cpp:418
int mChannelHeight
Definition: Export.h:254
int mFilterIndex
Definition: Export.h:215
Dialog for advanced mixing.
Definition: Export.h:266
FormatInfoArray mFormatInfos
Definition: Export.h:144
END_EVENT_TABLE()
void OnOk(wxCommandEvent &event)
Definition: Export.cpp:1374
TrackList * GetTracks()
Definition: Project.h:177
virtual ~Exporter()
Definition: Export.cpp:310
void SetMask(const wxString &mask, int index)
Definition: Export.cpp:139
static void InitProgress(std::unique_ptr< ProgressDialog > &pDialog, const wxString &title, const wxString &message)
Definition: Export.cpp:259
int mTrackHeight
Definition: Export.h:254
int mNumSelected
Definition: Export.h:218
std::vector< movable_ptr< ExportPlugin > > ExportPluginArray
Definition: Export.h:147
void SetMessage(const wxString &message)
int GetAutoExportSubFormat()
Definition: Export.cpp:983
static wxBrush playRegionBrush[2]
Definition: AColor.h:115
Panel that displays mixing for advanced mixing option.
Definition: Export.h:233
Class used with Mixer.
Definition: Mix.h:58
MixerSpec * mMixerSpec
Definition: Export.h:249
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:982