Audacity  3.0.3
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 
32 #include "Export.h"
33 
34 #include <wx/bmpbuttn.h>
35 #include <wx/dcclient.h>
36 #include <wx/file.h>
37 #include <wx/filectrl.h>
38 #include <wx/filename.h>
39 #include <wx/simplebook.h>
40 #include <wx/sizer.h>
41 #include <wx/slider.h>
42 #include <wx/statbox.h>
43 #include <wx/stattext.h>
44 #include <wx/string.h>
45 #include <wx/textctrl.h>
46 #include <wx/timer.h>
47 #include <wx/dcmemory.h>
48 #include <wx/window.h>
49 
50 #include "sndfile.h"
51 
52 #include "../widgets/FileDialog/FileDialog.h"
53 
54 #include "../src/AllThemeResources.h"
55 #include "../Mix.h"
56 #include "../Prefs.h"
57 #include "../prefs/ImportExportPrefs.h"
58 #include "../Project.h"
59 #include "../ProjectHistory.h"
60 #include "../ProjectSettings.h"
61 #include "../ProjectWindow.h"
62 #include "../ShuttleGui.h"
63 #include "../Tags.h"
64 #include "../Theme.h"
65 #include "../WaveTrack.h"
66 #include "../widgets/AudacityMessageBox.h"
67 #include "../widgets/Warning.h"
68 #include "../widgets/HelpSystem.h"
69 #include "../AColor.h"
70 #include "../FileNames.h"
71 #include "../widgets/HelpSystem.h"
72 #include "../widgets/ProgressDialog.h"
73 #include "../widgets/ErrorDialog.h"
74 #include "../wxFileNameWrapper.h"
75 
76 //----------------------------------------------------------------------------
77 // ExportPlugin
78 //----------------------------------------------------------------------------
79 
81 {
82 }
83 
85 {
86 }
87 
88 bool ExportPlugin::CheckFileName(wxFileName & WXUNUSED(filename), int WXUNUSED(format))
89 {
90  return true;
91 }
92 
101 {
102  FormatInfo nf;
103  mFormatInfos.push_back(nf);
104  return mFormatInfos.size();
105 }
106 
108 {
109  return mFormatInfos.size();
110 }
111 
116 void ExportPlugin::SetFormat(const wxString & format, int index)
117 {
118  mFormatInfos[index].mFormat = format;
119 }
120 
121 void ExportPlugin::SetDescription(const TranslatableString & description, int index)
122 {
123  mFormatInfos[index].mDescription = description;
124 }
125 
126 void ExportPlugin::AddExtension(const FileExtension &extension, int index)
127 {
128  mFormatInfos[index].mExtensions.push_back(extension);
129 }
130 
131 void ExportPlugin::SetExtensions(FileExtensions extensions, int index)
132 {
133  mFormatInfos[index].mExtensions = std::move(extensions);
134 }
135 
137 {
138  mFormatInfos[index].mMask = std::move( mask );
139 }
140 
141 void ExportPlugin::SetMaxChannels(unsigned maxchannels, unsigned index)
142 {
143  mFormatInfos[index].mMaxChannels = maxchannels;
144 }
145 
146 void ExportPlugin::SetCanMetaData(bool canmetadata, int index)
147 {
148  mFormatInfos[index].mCanMetaData = canmetadata;
149 }
150 
151 wxString ExportPlugin::GetFormat(int index)
152 {
153  return mFormatInfos[index].mFormat;
154 }
155 
157 {
158  return mFormatInfos[index].mDescription;
159 }
160 
162 {
163  return mFormatInfos[index].mExtensions[0];
164 }
165 
167 {
168  return mFormatInfos[index].mExtensions;
169 }
170 
172 {
173  if (!mFormatInfos[index].mMask.empty())
174  return mFormatInfos[index].mMask;
175 
176  return { { GetDescription(index), GetExtensions(index) } };
177 }
178 
179 unsigned ExportPlugin::GetMaxChannels(int index)
180 {
181  return mFormatInfos[index].mMaxChannels;
182 }
183 
185 {
186  return mFormatInfos[index].mCanMetaData;
187 }
188 
189 bool ExportPlugin::IsExtension(const FileExtension & ext, int index)
190 {
191  bool isext = false;
192  for (int i = index; i < GetFormatCount(); i = GetFormatCount())
193  {
194  const auto &defext = GetExtension(i);
195  const auto &defexts = GetExtensions(i);
196  int indofext = defexts.Index(ext, false);
197  if (defext.empty() || (indofext != wxNOT_FOUND))
198  isext = true;
199  }
200  return isext;
201 }
202 
203 bool ExportPlugin::DisplayOptions(wxWindow * WXUNUSED(parent), int WXUNUSED(format))
204 {
205  return false;
206 }
207 
209 {
210  S.StartHorizontalLay(wxCENTER);
211  {
212  S.StartHorizontalLay(wxCENTER, 0);
213  {
214  S.Prop(1).AddTitle(XO("No format specific options"));
215  }
216  S.EndHorizontalLay();
217  }
218  S.EndHorizontalLay();
219 }
220 
221 //Create a mixer by computing the time warp factor
222 std::unique_ptr<Mixer> ExportPlugin::CreateMixer(const TrackList &tracks,
223  bool selectionOnly,
224  double startTime, double stopTime,
225  unsigned numOutChannels, size_t outBufferSize, bool outInterleaved,
226  double outRate, sampleFormat outFormat,
227  MixerSpec *mixerSpec)
228 {
229  WaveTrackConstArray inputTracks;
230 
231  bool anySolo = !(( tracks.Any<const WaveTrack>() + &WaveTrack::GetSolo ).empty());
232 
233  auto range = tracks.Any< const WaveTrack >()
234  + (selectionOnly ? &Track::IsSelected : &Track::Any )
235  - ( anySolo ? &WaveTrack::GetNotSolo : &WaveTrack::GetMute);
236  for (auto pTrack: range)
237  inputTracks.push_back(
238  pTrack->SharedPointer< const WaveTrack >() );
239  // MB: the stop time should not be warped, this was a bug.
240  return std::make_unique<Mixer>(inputTracks,
241  // Throw, to stop exporting, if read fails:
242  true,
243  Mixer::WarpOptions{tracks},
244  startTime, stopTime,
245  numOutChannels, outBufferSize, outInterleaved,
246  outRate, outFormat,
247  true, mixerSpec);
248 }
249 
250 void ExportPlugin::InitProgress(std::unique_ptr<ProgressDialog> &pDialog,
251  const TranslatableString &title, const TranslatableString &message)
252 {
253  if (!pDialog)
254  pDialog = std::make_unique<ProgressDialog>( title, message );
255  else {
256  pDialog->SetTitle( title );
257  pDialog->SetMessage( message );
258  pDialog->Reinit();
259  }
260 }
261 
262 void ExportPlugin::InitProgress(std::unique_ptr<ProgressDialog> &pDialog,
263  const wxFileNameWrapper &title, const TranslatableString &message)
264 {
265  return InitProgress(
266  pDialog, Verbatim( title.GetName() ), message );
267 }
268 
269 //----------------------------------------------------------------------------
270 // Export
271 //----------------------------------------------------------------------------
272 
273 
274 wxDEFINE_EVENT(AUDACITY_FILE_SUFFIX_EVENT, wxCommandEvent);
275 
276 BEGIN_EVENT_TABLE(Exporter, wxEvtHandler)
277  EVT_FILECTRL_FILTERCHANGED(wxID_ANY, Exporter::OnFilterChanged)
278  EVT_BUTTON(wxID_HELP, Exporter::OnHelp)
279  EVT_COMMAND(wxID_ANY, AUDACITY_FILE_SUFFIX_EVENT, Exporter::OnExtensionChanged)
281 
282 namespace {
283 const auto PathStart = wxT("Exporters");
284 
286 {
287  static Registry::TransparentGroupItem<> registry{ PathStart };
288  return registry;
289 }
290 
294  : SingleItem{ id }
295  , mFactory{ factory }
296  {}
297 
299 };
300 
301  using ExportPluginFactories = std::vector< Exporter::ExportPluginFactory >;
303  {
304  static ExportPluginFactories theList;
305  return theList;
306  }
307 }
308 
310  const Identifier &id,
312  const Registry::Placement &placement )
313 {
314  if ( factory )
315  Registry::RegisterItem( sRegistry(), placement,
316  std::make_unique< ExporterItem >( id, factory ) );
317 }
318 
320 : mProject{ &project }
321 {
322  using namespace Registry;
323  static OrderingPreferenceInitializer init{
324  PathStart,
325  { {wxT(""), wxT("PCM,MP3,OGG,FLAC,MP2,CommandLine,FFmpeg") } },
326  };
327 
328  mMixerSpec = NULL;
329  mBook = NULL;
330 
331  // build the list of export plugins.
332  for ( const auto &factory : sFactories() )
333  mPlugins.emplace_back( factory() );
334 
335  struct MyVisitor final : Visitor {
336  MyVisitor()
337  {
338  // visit the registry to collect the plug-ins properly
339  // sorted
341  Registry::Visit( *this, &top, &sRegistry() );
342  }
343 
344  void Visit( SingleItem &item, const Path &path ) override
345  {
346  mPlugins.emplace_back(
347  static_cast<ExporterItem&>( item ).mFactory() );
348  }
349 
350  ExportPluginArray mPlugins;
351  } visitor;
352 
353  mPlugins.swap( visitor.mPlugins );
354 
355  SetFileDialogTitle( XO("Export Audio") );
356 }
357 
359 {
360 }
361 
362 void Exporter::OnExtensionChanged(wxCommandEvent &evt)
363 {
364  mDialog->SetFileExtension(evt.GetString().BeforeFirst(' ').Lower());
365 }
366 
367 void Exporter::OnHelp(wxCommandEvent& WXUNUSED(evt))
368 {
369  wxWindow * pWin = FindProjectFrame( mProject );
370  HelpSystem::ShowHelp(pWin, wxT("File_Export_Dialog"), true);
371 }
372 
374 {
375  // The default title is "Export File"
376  mFileDialogTitle = DialogTitle;
377 }
378 
379 int Exporter::FindFormatIndex(int exportindex)
380 {
381  int c = 0;
382  for (const auto &pPlugin : mPlugins)
383  {
384  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
385  {
386  if (exportindex == c) return j;
387  c++;
388  }
389  }
390  return 0;
391 }
392 
394 {
395  return mPlugins;
396 }
397 
399  const TranslatableString &title,
400  const TranslatableString &shortUndoDescription, bool force)
401 {
402  auto &settings = ProjectSettings::Get( project );
403  auto &tags = Tags::Get( project );
404 
405  // Back up my tags
406  // Tags (artist name, song properties, MP3 ID3 info, etc.)
407  // The structure may be shared with undo history entries
408  // To keep undo working correctly, always replace this with a NEW duplicate
409  // BEFORE doing any editing of it!
410  auto newTags = tags.Duplicate();
411 
412  if (newTags->ShowEditDialog(&GetProjectFrame( project ), title, force)) {
413  if (tags != *newTags) {
414  // Commit the change to project state only now.
415  Tags::Set( project, newTags );
416  ProjectHistory::Get( project ).PushState( title, shortUndoDescription);
417  }
418  bool bShowInFuture;
419  gPrefs->Read(wxT("/AudioFiles/ShowId3Dialog"), &bShowInFuture, true);
420  settings.SetShowId3Dialog( bShowInFuture );
421  return true;
422  }
423 
424  return false;
425 }
426 
427 bool Exporter::Process(bool selectedOnly, double t0, double t1)
428 {
429  // Save parms
430  mSelectedOnly = selectedOnly;
431  mT0 = t0;
432  mT1 = t1;
433 
434  // Gather track information
435  if (!ExamineTracks()) {
436  return false;
437  }
438 
439  // Ask user for file name
440  if (!GetFilename()) {
441  return false;
442  }
443 
444  // Check for down mixing
445  if (!CheckMix()) {
446  return false;
447  }
448 
449  // Let user edit MetaData
450  if (mPlugins[mFormat]->GetCanMetaData(mSubFormat)) {
451  if (!DoEditMetadata( *mProject,
452  XO("Edit Metadata Tags"), XO("Exported Tags"),
453  ProjectSettings::Get( *mProject ).GetShowId3Dialog())) {
454  return false;
455  }
456  }
457 
458  // Ensure filename doesn't interfere with project files.
459  if (!CheckFilename()) {
460  return false;
461  }
462 
463  // Export the tracks
464  bool success = ExportTracks();
465 
466  // Get rid of mixerspec
467  mMixerSpec.reset();
468 
469  if (success) {
470  if (mFormatName.empty()) {
471  gPrefs->Write(wxT("/Export/Format"), mPlugins[mFormat]->GetFormat(mSubFormat));
472  }
473 
474  FileNames::UpdateDefaultPath(FileNames::Operation::Export, mFilename.GetPath());
475  }
476 
477  return success;
478 }
479 
480 bool Exporter::Process(unsigned numChannels,
481  const FileExtension &type, const wxString & filename,
482  bool selectedOnly, double t0, double t1)
483 {
484  // Save parms
485  mChannels = numChannels;
486  mFilename = filename;
487  mSelectedOnly = selectedOnly;
488  mT0 = t0;
489  mT1 = t1;
491 
492  int i = -1;
493  for (const auto &pPlugin : mPlugins) {
494  ++i;
495  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
496  {
497  if (pPlugin->GetFormat(j).IsSameAs(type, false))
498  {
499  mFormat = i;
500  mSubFormat = j;
501  return CheckFilename() && ExportTracks();
502  }
503  }
504  }
505 
506  return false;
507 }
508 
510 {
511  // Init
512  mNumSelected = 0;
513  mNumLeft = 0;
514  mNumRight = 0;
515  mNumMono = 0;
516 
517  // First analyze the selected audio, perform sanity checks, and provide
518  // information as appropriate.
519 
520  // Tally how many are right, left, mono, and make sure at
521  // least one track is selected (if selectedOnly==true)
522 
523  double earliestBegin = mT1;
524  double latestEnd = mT0;
525 
526  auto &tracks = TrackList::Get( *mProject );
527 
528  bool anySolo = !(( tracks.Any<const WaveTrack>() + &WaveTrack::GetSolo ).empty());
529 
530  for (auto tr :
531  tracks.Any< const WaveTrack >()
533  - ( anySolo ? &WaveTrack::GetNotSolo : &WaveTrack::GetMute)
534  ) {
535  mNumSelected++;
536 
537  if (tr->GetChannel() == Track::LeftChannel) {
538  mNumLeft++;
539  }
540  else if (tr->GetChannel() == Track::RightChannel) {
541  mNumRight++;
542  }
543  else if (tr->GetChannel() == Track::MonoChannel) {
544  // It's a mono channel, but it may be panned
545  float pan = tr->GetPan();
546 
547  if (pan == -1.0)
548  mNumLeft++;
549  else if (pan == 1.0)
550  mNumRight++;
551  else if (pan == 0)
552  mNumMono++;
553  else {
554  // Panned partially off-center. Mix as stereo.
555  mNumLeft++;
556  mNumRight++;
557  }
558  }
559 
560  if (tr->GetOffset() < earliestBegin) {
561  earliestBegin = tr->GetOffset();
562  }
563 
564  if (tr->GetEndTime() > latestEnd) {
565  latestEnd = tr->GetEndTime();
566  }
567  }
568 
569  if (mNumSelected == 0) {
570  TranslatableString message;
571  if(mSelectedOnly)
572  message = XO("All selected audio is muted.");
573  else
574  message = XO("All audio is muted.");
576  ":576",
577  message);
578  return false;
579  }
580 
581  // The skipping of silent space could be cleverer and take
582  // into account clips.
583  // As implemented now, it can only skip initial silent space that
584  // has no clip before it, and terminal silent space that has no clip
585  // after it.
586  if (mT0 < earliestBegin){
587  // Bug 1904
588  // Previously we always skipped initial silent space.
589  // Now skipping it is an opt-in option.
590  bool skipSilenceAtBeginning;
591  gPrefs->Read(wxT("/AudioFiles/SkipSilenceAtBeginning"),
592  &skipSilenceAtBeginning, false);
593  if (skipSilenceAtBeginning)
594  mT0 = earliestBegin;
595  }
596 
597  // We still skip silent space at the end
598  if (mT1 > latestEnd)
599  mT1 = latestEnd;
600 
601  return true;
602 }
603 
605 {
606  mFormat = -1;
607 
608  FileNames::FileTypes fileTypes;
609  auto defaultFormat = mFormatName;
610  if( defaultFormat.empty() )
611  defaultFormat = gPrefs->Read(wxT("/Export/Format"),
612  wxT("WAV"));
613 
614  mFilterIndex = 0;
615 
616  {
617  int i = -1;
618  for (const auto &pPlugin : mPlugins) {
619  ++i;
620  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
621  {
622  auto mask = pPlugin->GetMask(j);
623  fileTypes.insert( fileTypes.end(), mask.begin(), mask.end() );
624  if (mPlugins[i]->GetFormat(j) == defaultFormat) {
625  mFormat = i;
626  mSubFormat = j;
627  }
628  if (mFormat == -1) mFilterIndex++;
629  }
630  }
631  }
632  if (mFormat == -1)
633  {
634  mFormat = 0;
635  mFilterIndex = 0;
636  mSubFormat = 0;
637  }
638  wxString defext = mPlugins[mFormat]->GetExtension(mSubFormat).Lower();
639 
640  //Bug 1304: Set a default path if none was given. For Export.
641  mFilename.SetPath(FileNames::FindDefaultPath(FileNames::Operation::Export));
642  mFilename.SetName(mProject->GetProjectName());
643  if (mFilename.GetName().empty())
644  mFilename.SetName(_("untitled"));
645  while (true) {
646  // Must reset each iteration
647  mBook = NULL;
648 
649  {
650  auto useFileName = mFilename;
651  if (!useFileName.HasExt())
652  useFileName.SetExt(defext);
655  mFilename.GetPath(),
656  useFileName.GetFullName(),
657  fileTypes,
658  wxFD_SAVE | wxRESIZE_BORDER);
659  mDialog = &fd;
660  mDialog->PushEventHandler(this);
661 
662  fd.SetUserPaneCreator(CreateUserPaneCallback, (wxUIntPtr) this);
664 
665  int result = fd.ShowModal();
666 
667  mDialog->PopEventHandler();
668 
669  if (result == wxID_CANCEL) {
670  return false;
671  }
672 
673  mFilename = fd.GetPath();
674  if (mFilename == wxT("")) {
675  return false;
676  }
677 
678  mFormat = fd.GetFilterIndex();
680  }
681 
682  {
683  int c = 0;
684  int i = -1;
685  for (const auto &pPlugin : mPlugins)
686  {
687  ++i;
688  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
689  {
690  if (mFilterIndex == c)
691  {
692  mFormat = i;
693  mSubFormat = j;
694  }
695  c++;
696  }
697  }
698  }
699 
700  const auto ext = mFilename.GetExt();
701  defext = mPlugins[mFormat]->GetExtension(mSubFormat).Lower();
702 
703  //
704  // Check the extension - add the default if it's not there,
705  // and warn user if it's abnormal.
706  //
707  if (ext.empty()) {
708  //
709  // Make sure the user doesn't accidentally save the file
710  // as an extension with no name, like just plain ".wav".
711  //
712  if (mFilename.GetName().Left(1) == wxT(".")) {
713  auto prompt =
714  XO("Are you sure you want to export the file as \"%s\"?\n")
715  .Format( mFilename.GetFullName() );
716 
717  int action = AudacityMessageBox(
718  prompt,
719  XO("Warning"),
720  wxYES_NO | wxICON_EXCLAMATION);
721  if (action != wxYES) {
722  continue;
723  }
724  }
725 
726  mFilename.SetExt(defext);
727  }
728 
729  if (!mPlugins[mFormat]->CheckFileName(mFilename, mSubFormat))
730  {
731  continue;
732  }
733  else if (!ext.empty() && !mPlugins[mFormat]->IsExtension(ext,mSubFormat) && ext.CmpNoCase(defext)) {
734  auto prompt = XO("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?")
735  .Format(mPlugins[mFormat]->GetFormat(mSubFormat),
736  mFilename.GetFullName(),
737  defext);
738 
739  int action = AudacityMessageBox(
740  prompt,
741  XO("Warning"),
742  wxYES_NO | wxICON_EXCLAMATION);
743  if (action != wxYES) {
744  continue;
745  }
746  }
747 
748  if (mFilename.GetFullPath().length() >= 256) {
750  XO( "Sorry, pathnames longer than 256 characters not supported.") );
751  continue;
752  }
753 
754 // For Mac, it's handled by the FileDialog
755 #if !defined(__WXMAC__)
756  if (mFilename.FileExists()) {
757  auto prompt = XO("A file named \"%s\" already exists. Replace?")
758  .Format( mFilename.GetFullPath() );
759 
760  int action = AudacityMessageBox(
761  prompt,
762  XO("Warning"),
763  wxYES_NO | wxICON_EXCLAMATION);
764  if (action != wxYES) {
765  continue;
766  }
767  }
768 #endif
769 
770  break;
771  }
772 
773  return true;
774 }
775 
776 //
777 // For safety, if the file already exists it stores the filename
778 // the user wants in actualName, and returns a temporary file name.
779 // The calling function should rename the file when it's successfully
780 // exported.
781 //
783 {
784  //
785  // To be even safer, return a temporary file name based
786  // on this one...
787  //
788 
790 
791  int suffix = 0;
792  while (mFilename.FileExists()) {
793  mFilename.SetName(mActualName.GetName() +
794  wxString::Format(wxT("%d"), suffix));
795  suffix++;
796  }
797 
798  return true;
799 }
800 
802 {
803  int c = 0;
804  int mf = -1, msf = -1;
805  int i = -1;
806  for (const auto &pPlugin : mPlugins)
807  {
808  ++i;
809  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
810  {
811  if (index == c)
812  {
813  mf = i;
814  msf = j;
815  }
816  c++;
817  }
818  }
819  // This shouldn't happen...
820  if (index >= c) {
821  return;
822  }
823 
824 #if defined(__WXMSW__)
825  mPlugins[mf]->DisplayOptions( FindProjectFrame( mProject ), msf);
826 #else
827  mPlugins[mf]->DisplayOptions(mDialog, msf);
828 #endif
829 }
830 
831 bool Exporter::CheckMix(bool prompt /*= true*/ )
832 {
833  // Clean up ... should never happen
834  mMixerSpec.reset();
835 
836  // Determine if exported file will be stereo or mono or multichannel,
837  // and if mixing will occur.
838 
840  int exportedChannels = mPlugins[mFormat]->SetNumExportChannels();
841 
842  if (downMix) {
843  if (mNumRight > 0 || mNumLeft > 0) {
844  mChannels = 2;
845  }
846  else {
847  mChannels = 1;
848  }
850  mPlugins[mFormat]->GetMaxChannels(mSubFormat));
851 
852  auto numLeft = mNumLeft + mNumMono;
853  auto numRight = mNumRight + mNumMono;
854 
855  if (numLeft > 1 || numRight > 1 || mNumLeft + mNumRight + mNumMono > mChannels) {
856  wxString exportFormat = mPlugins[mFormat]->GetFormat(mSubFormat);
857  if (exportFormat != wxT("CL") && exportFormat != wxT("FFMPEG") && exportedChannels == -1)
858  exportedChannels = mChannels;
859 
860  if (prompt) {
861  auto pWindow = ProjectWindow::Find(mProject);
862  if (exportedChannels == 1) {
863  if (ShowWarningDialog(pWindow,
864  wxT("MixMono"),
865  XO("Your tracks will be mixed down and exported as one mono file."),
866  true) == wxID_CANCEL)
867  return false;
868  }
869  else if (exportedChannels == 2) {
870  if (ShowWarningDialog(pWindow,
871  wxT("MixStereo"),
872  XO("Your tracks will be mixed down and exported as one stereo file."),
873  true) == wxID_CANCEL)
874  return false;
875  }
876  else {
877  if (ShowWarningDialog(pWindow,
878  wxT("MixUnknownChannels"),
879  XO("Your tracks will be mixed down to one exported file according to the encoder settings."),
880  true) == wxID_CANCEL)
881  return false;
882  }
883  }
884  }
885  }
886  else
887  {
888  if (exportedChannels < 0)
889  exportedChannels = mPlugins[mFormat]->GetMaxChannels(mSubFormat);
890 
893  exportedChannels,
894  NULL,
895  1,
896  XO("Advanced Mixing Options"));
897  if (prompt) {
898  if (md.ShowModal() != wxID_OK) {
899  return false;
900  }
901  }
902 
903  mMixerSpec = std::make_unique<MixerSpec>(*(md.GetMixerSpec()));
904  mChannels = mMixerSpec->GetNumChannels();
905  }
906 
907  return true;
908 }
909 
911 {
912  // Keep original in case of failure
913  if (mActualName != mFilename) {
914  ::wxRenameFile(mActualName.GetFullPath(), mFilename.GetFullPath());
915  }
916 
917  bool success = false;
918 
919  auto cleanup = finally( [&] {
920  if (mActualName != mFilename) {
921  // Remove backup
922  if ( success )
923  ::wxRemoveFile(mFilename.GetFullPath());
924  else {
925  // Restore original, if needed
926  ::wxRemoveFile(mActualName.GetFullPath());
927  ::wxRenameFile(mFilename.GetFullPath(), mActualName.GetFullPath());
928  }
929  // Restore filename
931  }
932  else {
933  if ( ! success )
934  // Remove any new, and only partially written, file.
935  ::wxRemoveFile(mFilename.GetFullPath());
936  }
937  } );
938 
939  std::unique_ptr<ProgressDialog> pDialog;
940  auto result = mPlugins[mFormat]->Export(mProject,
941  pDialog,
942  mChannels,
943  mActualName.GetFullPath(),
945  mT0,
946  mT1,
947  mMixerSpec.get(),
948  NULL,
949  mSubFormat);
950 
951  success =
952  result == ProgressResult::Success || result == ProgressResult::Stopped;
953 
954  return success;
955 }
956 
957 void Exporter::CreateUserPaneCallback(wxWindow *parent, wxUIntPtr userdata)
958 {
959  Exporter *self = (Exporter *) userdata;
960  if (self)
961  {
962  self->CreateUserPane(parent);
963  }
964 }
965 
966 void Exporter::CreateUserPane(wxWindow *parent)
967 {
968  ShuttleGui S(parent, eIsCreating);
969 
970  S.StartStatic(XO("Format Options"), 1);
971  {
972  S.StartHorizontalLay(wxEXPAND);
973  {
974  mBook = S.Position(wxEXPAND).StartSimplebook();
975  {
976  for (const auto &pPlugin : mPlugins)
977  {
978  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
979  {
980  // Name of simple book page is not displayed
981  S.StartNotebookPage( {} );
982  {
983  pPlugin->OptionsCreate(S, j);
984  }
985  S.EndNotebookPage();
986  }
987  }
988  }
989  S.EndSimplebook();
990 
991  auto b = safenew wxBitmapButton(S.GetParent(), wxID_HELP, theTheme.Bitmap( bmpHelpIcon ));
992  b->SetToolTip( XO("Help").Translation() );
993  b->SetLabel(XO("Help").Translation()); // for screen readers
994  S.Position(wxALIGN_BOTTOM | wxRIGHT | wxBOTTOM).AddWindow(b);
995  }
996  S.EndHorizontalLay();
997  }
998  S.EndStatic();
999 
1000  return;
1001 }
1002 
1003 void Exporter::OnFilterChanged(wxFileCtrlEvent & evt)
1004 {
1005  int index = evt.GetFilterIndex();
1006 
1007  // On GTK, this event can fire before the userpane is created
1008  if (mBook == NULL || index < 0 || index >= (int) mBook->GetPageCount())
1009  {
1010  return;
1011  }
1012 
1013 #if defined(__WXGTK__)
1014  // On Windows and MacOS, changing the filter in the dialog
1015  // automatically changes the extension of the current file
1016  // name. GTK doesn't, so do it here.
1017  {
1018  FileNames::FileTypes fileTypes;
1019 
1020  int i = -1;
1021  for (const auto &pPlugin : mPlugins)
1022  {
1023  ++i;
1024  for (int j = 0; j < pPlugin->GetFormatCount(); j++)
1025  {
1026  auto mask = pPlugin->GetMask(j);
1027  fileTypes.insert( fileTypes.end(), mask.begin(), mask.end() );
1028  }
1029  }
1030 
1031  if (index < fileTypes.size())
1032  {
1033  mDialog->SetFileExtension(fileTypes[index].extensions[0].Lower());
1034  }
1035  }
1036 #endif
1037 
1038  mBook->ChangeSelection(index);
1039 }
1040 
1042  double t0,
1043  double t1,
1044  wxFileName fnFile,
1045  int iFormat,
1046  int iSubFormat,
1047  int iFilterIndex)
1048 {
1049  // Save parms
1050  mSelectedOnly = selectedOnly;
1051  mT0 = t0;
1052  mT1 = t1;
1053 
1054  // Auto Export Parameters
1055  mFilename = fnFile;
1056  mFormat = iFormat;
1057  mSubFormat = iSubFormat;
1058  mFilterIndex = iFilterIndex;
1059 
1060  // Gather track information
1061  if (!ExamineTracks()) {
1062  return false;
1063  }
1064 
1065  // Check for down mixing
1066  if (!CheckMix(false)) {
1067  return false;
1068  }
1069 
1070  // Ensure filename doesn't interfere with project files.
1071  if (!CheckFilename()) {
1072  return false;
1073  }
1074 
1075  // Export the tracks
1076  bool success = ExportTracks();
1077 
1078  // Get rid of mixerspec
1079  mMixerSpec.reset();
1080 
1081  return success;
1082 }
1083 
1085  return mFormat;
1086 }
1087 
1089  return mSubFormat;
1090 }
1091 
1093  return mFormat;
1094 }
1095 
1097  return mFilename;
1098 }
1099 
1101  mFormat = -1;
1102 
1103  if( GetFilename()==false )
1104  return false;
1105 
1106  // Let user edit MetaData
1107  if (mPlugins[mFormat]->GetCanMetaData(mSubFormat)) {
1108  if (!DoEditMetadata( *mProject,
1109  XO("Edit Metadata Tags"),
1110  XO("Exported Tags"),
1111  ProjectSettings::Get(*mProject).GetShowId3Dialog())) {
1112  return false;
1113  }
1114  }
1115 
1116  return true;
1117 }
1118 
1119 //----------------------------------------------------------------------------
1120 // ExportMixerPanel
1121 //----------------------------------------------------------------------------
1122 
1123 BEGIN_EVENT_TABLE(ExportMixerPanel, wxPanelWrapper)
1124  EVT_PAINT(ExportMixerPanel::OnPaint)
1125  EVT_MOUSE_EVENTS(ExportMixerPanel::OnMouseEvent)
1127 
1128 ExportMixerPanel::ExportMixerPanel( wxWindow *parent, wxWindowID id,
1129  MixerSpec *mixerSpec,
1130  wxArrayString trackNames,
1131  const wxPoint& pos, const wxSize& size):
1132  wxPanelWrapper(parent, id, pos, size)
1133  , mMixerSpec{mixerSpec}
1134  , mChannelRects{ mMixerSpec->GetMaxNumChannels() }
1135  , mTrackRects{ mMixerSpec->GetNumTracks() }
1136 {
1137  mBitmap = NULL;
1138  mWidth = 0;
1139  mHeight = 0;
1140  mSelectedTrack = mSelectedChannel = -1;
1141 
1142  mTrackNames = trackNames;
1143 }
1144 
1146 {
1147 }
1148 
1149 //set the font on memDC such that text can fit in specified width and height
1150 void ExportMixerPanel::SetFont(wxMemoryDC &memDC, const wxString &text, int width,
1151  int height )
1152 {
1153  int l = 0, u = 13, m, w, h;
1154  wxFont font = memDC.GetFont();
1155  while( l < u - 1 )
1156  {
1157  m = ( l + u ) / 2;
1158  font.SetPointSize( m );
1159  memDC.SetFont( font );
1160  memDC.GetTextExtent( text, &w, &h );
1161 
1162  if( w < width && h < height )
1163  l = m;
1164  else
1165  u = m;
1166  }
1167  font.SetPointSize( l );
1168  memDC.SetFont( font );
1169 }
1170 
1171 void ExportMixerPanel::OnPaint(wxPaintEvent & WXUNUSED(event))
1172 {
1173  wxPaintDC dc( this );
1174 
1175  int width, height;
1176  GetSize( &width, &height );
1177 
1178  if( !mBitmap || mWidth != width || mHeight != height )
1179  {
1180  mWidth = width;
1181  mHeight = height;
1182  mBitmap = std::make_unique<wxBitmap>( mWidth, mHeight,24 );
1183  }
1184 
1185  wxColour bkgnd = GetBackgroundColour();
1186  wxBrush bkgndBrush( bkgnd, wxBRUSHSTYLE_SOLID );
1187 
1188  wxMemoryDC memDC;
1189  memDC.SelectObject( *mBitmap );
1190 
1191  //draw background
1192  wxRect bkgndRect;
1193  bkgndRect.x = 0;
1194  bkgndRect.y = 0;
1195  bkgndRect.width = mWidth;
1196  bkgndRect.height = mHeight;
1197 
1198  memDC.SetBrush( *wxWHITE_BRUSH );
1199  memDC.SetPen( *wxBLACK_PEN );
1200  memDC.DrawRectangle( bkgndRect );
1201 
1202  //box dimensions
1203  mBoxWidth = mWidth / 6;
1204 
1205  mTrackHeight = ( mHeight * 3 ) / ( mMixerSpec->GetNumTracks() * 4 );
1206  if( mTrackHeight > 30 )
1207  mTrackHeight = 30;
1208 
1209  mChannelHeight = ( mHeight * 3 ) / ( mMixerSpec->GetNumChannels() * 4 );
1210  if( mChannelHeight > 30 )
1211  mChannelHeight = 30;
1212 
1213  static double PI = 2 * acos( 0.0 );
1214  double angle = atan( ( 3.0 * mHeight ) / mWidth );
1215  double radius = mHeight / ( 2.0 * sin( PI - 2.0 * angle ) );
1216  double totAngle = ( asin( mHeight / ( 2.0 * radius ) ) * 2.0 );
1217 
1218  //draw tracks
1219  memDC.SetBrush( AColor::envelopeBrush );
1220  angle = totAngle / ( mMixerSpec->GetNumTracks() + 1 );
1221 
1222  int max = 0, w, h;
1223  for( unsigned int i = 1; i < mMixerSpec->GetNumTracks(); i++ )
1224  if( mTrackNames[ i ].length() > mTrackNames[ max ].length() )
1225  max = i;
1226 
1227  SetFont( memDC, mTrackNames[ max ], mBoxWidth, mTrackHeight );
1228 
1229  for( unsigned int i = 0; i < mMixerSpec->GetNumTracks(); i++ )
1230  {
1231  mTrackRects[ i ].x = (int)( mBoxWidth * 2 + radius - radius *
1232  cos( totAngle / 2.0 - angle * ( i + 1 ) ) - mBoxWidth + 0.5 );
1233  mTrackRects[ i ].y = (int)( mHeight * 0.5 - radius *
1234  sin( totAngle * 0.5 - angle * ( i + 1.0 ) ) -
1235  0.5 * mTrackHeight + 0.5 );
1236 
1237  mTrackRects[ i ].width = mBoxWidth;
1238  mTrackRects[ i ].height = mTrackHeight;
1239 
1240  memDC.SetPen( mSelectedTrack == (int)i ? *wxRED_PEN : *wxBLACK_PEN );
1241  memDC.DrawRectangle( mTrackRects[ i ] );
1242 
1243  memDC.GetTextExtent( mTrackNames[ i ], &w, &h );
1244  memDC.DrawText( mTrackNames[ i ],
1245  mTrackRects[ i ].x + ( mBoxWidth - w ) / 2,
1246  mTrackRects[ i ].y + ( mTrackHeight - h ) / 2 );
1247  }
1248 
1249  //draw channels
1250  memDC.SetBrush( AColor::playRegionBrush[ 0 ] );
1251  angle = ( asin( mHeight / ( 2.0 * radius ) ) * 2.0 ) /
1252  ( mMixerSpec->GetNumChannels() + 1 );
1253 
1254  SetFont( memDC, wxT( "Channel: XX" ), mBoxWidth, mChannelHeight );
1255  memDC.GetTextExtent( wxT( "Channel: XX" ), &w, &h );
1256 
1257  for( unsigned int i = 0; i < mMixerSpec->GetNumChannels(); i++ )
1258  {
1259  mChannelRects[ i ].x = (int)( mBoxWidth * 4 - radius + radius *
1260  cos( totAngle * 0.5 - angle * ( i + 1 ) ) + 0.5 );
1261  mChannelRects[ i ].y = (int)( mHeight * 0.5 - radius *
1262  sin( totAngle * 0.5 - angle * ( i + 1 ) ) -
1263  0.5 * mChannelHeight + 0.5 );
1264 
1265  mChannelRects[ i ].width = mBoxWidth;
1266  mChannelRects[ i ].height = mChannelHeight;
1267 
1268  memDC.SetPen( mSelectedChannel == (int)i ? *wxRED_PEN : *wxBLACK_PEN );
1269  memDC.DrawRectangle( mChannelRects[ i ] );
1270 
1271  memDC.DrawText( wxString::Format( _( "Channel: %2d" ), i + 1 ),
1272  mChannelRects[ i ].x + ( mBoxWidth - w ) / 2,
1273  mChannelRects[ i ].y + ( mChannelHeight - h ) / 2 );
1274  }
1275 
1276  //draw links
1277  memDC.SetPen( wxPen( *wxBLACK, mHeight / 200 ) );
1278  for( unsigned int i = 0; i < mMixerSpec->GetNumTracks(); i++ )
1279  for( unsigned int j = 0; j < mMixerSpec->GetNumChannels(); j++ )
1280  if( mMixerSpec->mMap[ i ][ j ] )
1281  AColor::Line(memDC, mTrackRects[ i ].x + mBoxWidth,
1282  mTrackRects[ i ].y + mTrackHeight / 2, mChannelRects[ j ].x,
1283  mChannelRects[ j ].y + mChannelHeight / 2 );
1284 
1285  dc.Blit( 0, 0, mWidth, mHeight, &memDC, 0, 0, wxCOPY, FALSE );
1286 }
1287 
1288 double ExportMixerPanel::Distance( wxPoint &a, wxPoint &b )
1289 {
1290  return sqrt( pow( a.x - b.x, 2.0 ) + pow( a.y - b.y, 2.0 ) );
1291 }
1292 
1293 //checks if p is on the line connecting la, lb with tolerance
1294 bool ExportMixerPanel::IsOnLine( wxPoint p, wxPoint la, wxPoint lb )
1295 {
1296  return Distance( p, la ) + Distance( p, lb ) - Distance( la, lb ) < 0.1;
1297 }
1298 
1299 void ExportMixerPanel::OnMouseEvent(wxMouseEvent & event)
1300 {
1301  if( event.ButtonDown() )
1302  {
1303  bool reset = true;
1304  //check tracks
1305  for( unsigned int i = 0; i < mMixerSpec->GetNumTracks(); i++ )
1306  if( mTrackRects[ i ].Contains( event.m_x, event.m_y ) )
1307  {
1308  reset = false;
1309  if( mSelectedTrack == (int)i )
1310  mSelectedTrack = -1;
1311  else
1312  {
1313  mSelectedTrack = i;
1314  if( mSelectedChannel != -1 )
1317  }
1318  goto found;
1319  }
1320 
1321  //check channels
1322  for( unsigned int i = 0; i < mMixerSpec->GetNumChannels(); i++ )
1323  if( mChannelRects[ i ].Contains( event.m_x, event.m_y ) )
1324  {
1325  reset = false;
1326  if( mSelectedChannel == (int)i )
1327  mSelectedChannel = -1;
1328  else
1329  {
1330  mSelectedChannel = i;
1331  if( mSelectedTrack != -1 )
1334  }
1335  goto found;
1336  }
1337 
1338  //check links
1339  for( unsigned int i = 0; i < mMixerSpec->GetNumTracks(); i++ )
1340  for( unsigned int j = 0; j < mMixerSpec->GetNumChannels(); j++ )
1341  if( mMixerSpec->mMap[ i ][ j ] && IsOnLine( wxPoint( event.m_x,
1342  event.m_y ), wxPoint( mTrackRects[ i ].x + mBoxWidth,
1343  mTrackRects[ i ].y + mTrackHeight / 2 ),
1344  wxPoint( mChannelRects[ j ].x, mChannelRects[ j ].y +
1345  mChannelHeight / 2 ) ) )
1346  mMixerSpec->mMap[ i ][ j ] = false;
1347 
1348 found:
1349  if( reset )
1351  Refresh( false );
1352  }
1353 }
1354 
1355 //----------------------------------------------------------------------------
1356 // ExportMixerDialog
1357 //----------------------------------------------------------------------------
1358 
1359 enum
1360 {
1361  ID_MIXERPANEL = 10001,
1363 };
1364 
1365 BEGIN_EVENT_TABLE( ExportMixerDialog, wxDialogWrapper )
1367  EVT_BUTTON( wxID_CANCEL, ExportMixerDialog::OnCancel )
1369  EVT_SIZE( ExportMixerDialog::OnSize )
1372 
1373 ExportMixerDialog::ExportMixerDialog( const TrackList *tracks, bool selectedOnly,
1374  unsigned maxNumChannels, wxWindow *parent, wxWindowID id, const TranslatableString &title,
1375  const wxPoint &position, const wxSize& size, long style ) :
1376  wxDialogWrapper( parent, id, title, position, size, style | wxRESIZE_BORDER )
1377 {
1378  SetName();
1379 
1380  unsigned numTracks = 0;
1381 
1382  bool anySolo = !(( tracks->Any<const WaveTrack>() + &WaveTrack::GetSolo ).empty());
1383 
1384  for (auto t :
1385  tracks->Any< const WaveTrack >()
1386  + ( selectedOnly ? &Track::IsSelected : &Track::Any )
1387  - ( anySolo ? &WaveTrack::GetNotSolo : &WaveTrack::GetMute)
1388  ) {
1389  numTracks++;
1390  const wxString sTrackName = (t->GetName()).Left(20);
1391  if( t->GetChannel() == Track::LeftChannel )
1392  /* i18n-hint: track name and L abbreviating Left channel */
1393  mTrackNames.push_back( wxString::Format( _( "%s - L" ), sTrackName ) );
1394  else if( t->GetChannel() == Track::RightChannel )
1395  /* i18n-hint: track name and R abbreviating Right channel */
1396  mTrackNames.push_back( wxString::Format( _( "%s - R" ), sTrackName ) );
1397  else
1398  mTrackNames.push_back(sTrackName);
1399  }
1400 
1401  // JKC: This is an attempt to fix a 'watching brief' issue, where the slider is
1402  // sometimes not slidable. My suspicion is that a mixer may incorrectly
1403  // state the number of channels - so we assume there are always at least two.
1404  // The downside is that if someone is exporting to a mono device, the dialog
1405  // will allow them to output to two channels. Hmm. We may need to revisit this.
1406 
1407  if (maxNumChannels < 2 )
1408  // STF (April 2016): AMR (narrowband) and MP3 may export 1 channel.
1409  // maxNumChannels = 2;
1410  maxNumChannels = 1;
1411  if (maxNumChannels > 32)
1412  maxNumChannels = 32;
1413 
1414  mMixerSpec = std::make_unique<MixerSpec>(numTracks, maxNumChannels);
1415 
1416  auto label = XO("Output Channels: %2d")
1417  .Format( mMixerSpec->GetNumChannels() );
1418 
1419  ShuttleGui S{ this, eIsCreating };
1420  {
1421  S.SetBorder( 5 );
1422 
1423  auto mixerPanel = safenew ExportMixerPanel(
1424  S.GetParent(), ID_MIXERPANEL, mMixerSpec.get(),
1425  mTrackNames, wxDefaultPosition, wxSize(400, -1));
1426  S.Prop(1)
1427  .Name(XO("Mixer Panel"))
1428  .Position(wxEXPAND | wxALL)
1429  .AddWindow(mixerPanel);
1430 
1431  S.StartHorizontalLay(wxALIGN_CENTER | wxALL, 0);
1432  {
1433  mChannelsText = S.AddVariableText(
1434  label,
1435  false, wxALIGN_LEFT | wxALL );
1436 
1437  S
1439  .Name(label)
1440  .Size({300, -1})
1441  .Style(wxSL_HORIZONTAL)
1442  .Position(wxEXPAND | wxALL)
1443  .AddSlider( {},
1444  mMixerSpec->GetNumChannels(),
1445  mMixerSpec->GetMaxNumChannels(), 1 );
1446  }
1447  S.EndHorizontalLay();
1448 
1450  }
1451 
1452  SetAutoLayout(true);
1453  GetSizer()->Fit( this );
1454  GetSizer()->SetSizeHints( this );
1455 
1456  SetSizeHints( 640, 480, 20000, 20000 );
1457 
1458  SetSize( 640, 480 );
1459  Center();
1460 }
1461 
1463 {
1464 }
1465 
1466 void ExportMixerDialog::OnSize(wxSizeEvent &event)
1467 {
1468  ExportMixerPanel *pnl = ( ( ExportMixerPanel* ) FindWindow( ID_MIXERPANEL ) );
1469  pnl->Refresh( false );
1470  event.Skip();
1471 }
1472 
1473 void ExportMixerDialog::OnSlider( wxCommandEvent & WXUNUSED(event))
1474 {
1475  wxSlider *channels = ( wxSlider* )FindWindow( ID_SLIDER_CHANNEL );
1476  ExportMixerPanel *pnl = ( ( ExportMixerPanel* ) FindWindow( ID_MIXERPANEL ) );
1477  mMixerSpec->SetNumChannels( channels->GetValue() );
1478  pnl->Refresh( false );
1479  wxString label;
1480  label.Printf( _( "Output Channels: %2d" ), mMixerSpec->GetNumChannels() );
1481  mChannelsText->SetLabel( label );
1482  channels->SetName( label );
1483 }
1484 
1485 void ExportMixerDialog::OnOk(wxCommandEvent & WXUNUSED(event))
1486 {
1487  EndModal( wxID_OK );
1488 }
1489 
1490 void ExportMixerDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
1491 {
1492  EndModal( wxID_CANCEL );
1493 }
1494 
1495 void ExportMixerDialog::OnMixerPanelHelp(wxCommandEvent & WXUNUSED(event))
1496 {
1497  HelpSystem::ShowHelp(this, wxT("Advanced_Mixing_Options"), true);
1498 }
1499 
1500 
1502 {
1503  return XO("Warning");
1504 }
1506 {
1507  return XO("Unable to export.\nError %s");
1508 }
1509 
1510 
1511 // This creates a generic export error dialog
1512 // Untranslated ErrorCodes like "MP3:1882" are used since we don't yet have
1513 // a good user facing error message. They allow us to
1514 // distinguish where the error occurred, and we can update the landing
1515 // page as we learn more about when (if ever) these errors actually happen.
1516 // The number happens to at one time have been a line number, but all
1517 // we need from them is that they be distinct.
1518 void ShowExportErrorDialog(wxString ErrorCode,
1519  TranslatableString message,
1520  const TranslatableString& caption)
1521 {
1522  ShowErrorDialog(nullptr,
1523  caption,
1524  message.Format( ErrorCode ),
1525  "Error:_Unable_to_export" // URL.
1526  );
1527 }
1528 
1530 {
1531  ShowErrorDialog(nullptr,
1532  XO("Warning"),
1534  "Error:_Disk_full_or_not_writable"
1535  );
1536 }
1537 
1538 
Exporter::OnHelp
void OnHelp(wxCommandEvent &evt)
Definition: Export.cpp:367
EVT_BUTTON
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
Registry::TransparentGroupItem
Definition: Registry.h:210
FileExtension
wxString FileExtension
Definition: Types.h:267
TranslatableString
Definition: Types.h:290
ShuttleGuiBase::EndSimplebook
void EndSimplebook()
Definition: ShuttleGui.cpp:1054
Registry::SingleItem
Definition: Registry.h:120
WaveTrackConstArray
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
Definition: AudioIO.h:57
ExportPlugin::GetMask
FileNames::FileTypes GetMask(int index)
Definition: Export.cpp:171
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:36
ExportMixerDialog::mChannelsText
wxStaticText * mChannelsText
Definition: Export.h:311
Registry
Definition: Menus.h:36
ExportMixerPanel::mTrackNames
wxArrayString mTrackNames
Definition: Export.h:284
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:68
anonymous_namespace{Export.cpp}::ExporterItem::ExporterItem
ExporterItem(const Identifier &id, const Exporter::ExportPluginFactory &factory)
Definition: Export.cpp:292
Exporter::Process
bool Process(bool selectedOnly, double t0, double t1)
Definition: Export.cpp:427
ExportMixerPanel::~ExportMixerPanel
virtual ~ExportMixerPanel()
Definition: Export.cpp:1145
Track::RightChannel
static const auto RightChannel
Definition: Track.h:266
PlayableTrack::GetSolo
bool GetSolo() const
Definition: Track.h:844
PlayableTrack::GetMute
bool GetMute() const
Definition: Track.h:843
ID_MIXERPANEL
@ ID_MIXERPANEL
Definition: Export.cpp:1361
ExportMixerDialog::OnOk
void OnOk(wxCommandEvent &event)
Definition: Export.cpp:1485
wxFileNameWrapper
Definition: wxFileNameWrapper.h:21
ShuttleGuiBase::AddTitle
void AddTitle(const TranslatableString &Prompt, int wrapWidth=0)
Centred text string.
Definition: ShuttleGui.cpp:274
Exporter::OnExtensionChanged
void OnExtensionChanged(wxCommandEvent &evt)
Definition: Export.cpp:362
Exporter::~Exporter
virtual ~Exporter()
Definition: Export.cpp:358
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:67
EVT_COMMAND
EVT_COMMAND(wxID_ANY, EVT_FREQUENCYTEXTCTRL_UPDATED, LabelDialog::OnFreqUpdate) LabelDialog
Definition: LabelDialog.cpp:91
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: AudacityMessageBox.h:20
ExportPlugin::GetFormatCount
virtual int GetFormatCount()
Definition: Export.cpp:107
Exporter::SetFileDialogTitle
void SetFileDialogTitle(const TranslatableString &DialogTitle)
Definition: Export.cpp:373
anonymous_namespace{Export.cpp}::ExporterItem
Definition: Export.cpp:291
Exporter::GetFilename
bool GetFilename()
Definition: Export.cpp:604
ExportPlugin::AddExtension
void AddExtension(const FileExtension &extension, int index)
Definition: Export.cpp:126
ShowExportErrorDialog
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption)
Definition: Export.cpp:1518
FileNames::UpdateDefaultPath
AUDACITY_DLL_API void UpdateDefaultPath(Operation op, const FilePath &path)
wxPanelWrapper
Definition: wxPanelWrapper.h:41
Exporter::CreateUserPane
void CreateUserPane(wxWindow *parent)
Definition: Export.cpp:966
Exporter::Exporter
Exporter(AudacityProject &project)
Definition: Export.cpp:319
ShowWarningDialog
int ShowWarningDialog(wxWindow *parent, const wxString &internalDialogName, const TranslatableString &message, bool showCancelButton, const TranslatableString &footer)
Definition: Warning.cpp:93
ExportMixerPanel::mBoxWidth
int mBoxWidth
Definition: Export.h:285
Mixer::WarpOptions
Definition: Mix.h:83
TrackList
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1263
Exporter::mChannels
unsigned mChannels
Definition: Export.h:252
ImportExportPrefs::ExportDownMixSetting
static EnumSetting< bool > ExportDownMixSetting
Definition: ImportExportPrefs.h:29
Exporter
Definition: Export.h:165
AColor::Line
static void Line(wxDC &dc, wxCoord x1, wxCoord y1, wxCoord x2, wxCoord y2)
Definition: AColor.cpp:112
Exporter::mActualName
wxFileName mActualName
Definition: Export.h:241
ExportMixerPanel::SetFont
void SetFont(wxMemoryDC &memDC, const wxString &text, int width, int height)
Definition: Export.cpp:1150
Exporter::SetAutoExportOptions
bool SetAutoExportOptions()
Definition: Export.cpp:1100
ExportMixerPanel::mBitmap
std::unique_ptr< wxBitmap > mBitmap
Definition: Export.h:276
ExportMixerDialog::OnCancel
void OnCancel(wxCommandEvent &event)
Definition: Export.cpp:1490
Exporter::CreateUserPaneCallback
static void CreateUserPaneCallback(wxWindow *parent, wxUIntPtr userdata)
Definition: Export.cpp:957
XO
#define XO(s)
Definition: Internat.h:32
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:39
ExportMixerPanel::mChannelRects
ArrayOf< wxRect > mChannelRects
Definition: Export.h:281
ExportMixerPanel::mTrackHeight
int mTrackHeight
Definition: Export.h:285
Exporter::GetAutoExportFormat
int GetAutoExportFormat()
Definition: Export.cpp:1084
ExportPlugin::ExportPlugin
ExportPlugin()
Definition: Export.cpp:80
Exporter::CheckFilename
bool CheckFilename()
Definition: Export.cpp:782
eCancelButton
@ eCancelButton
Definition: ShuttleGui.h:608
Track::Any
bool Any() const
Definition: Track.cpp:361
ExportMixerPanel::OnMouseEvent
void OnMouseEvent(wxMouseEvent &event)
Definition: Export.cpp:1299
ExportPlugin::GetCanMetaData
virtual bool GetCanMetaData(int index)
Definition: Export.cpp:184
ShuttleGuiBase::StartNotebookPage
wxNotebookPage * StartNotebookPage(const TranslatableString &Name)
Definition: ShuttleGui.cpp:1061
Exporter::mFormat
int mFormat
Definition: Export.h:246
PI
#define PI
Definition: Biquad.cpp:17
ExportPlugin::mFormatInfos
std::vector< FormatInfo > mFormatInfos
Definition: Export.h:151
anonymous_namespace{Export.cpp}::ExporterItem::mFactory
Exporter::ExportPluginFactory mFactory
Definition: Export.cpp:298
Exporter::mFilename
wxFileName mFilename
Definition: Export.h:240
ShuttleGuiBase::EndNotebookPage
void EndNotebookPage()
Definition: ShuttleGui.cpp:1084
ExportMixerPanel::mMixerSpec
MixerSpec * mMixerSpec
Definition: Export.h:280
wxArrayStringEx
Definition: MemoryX.h:663
Registry::Placement
Definition: Registry.h:219
Exporter::GetAutoExportFileName
wxFileName GetAutoExportFileName()
Definition: Export.cpp:1096
wxDEFINE_EVENT
wxDEFINE_EVENT(AUDACITY_FILE_SUFFIX_EVENT, wxCommandEvent)
Exporter::mSelectedOnly
bool mSelectedOnly
Definition: Export.h:253
ExportMixerPanel::Distance
double Distance(wxPoint &a, wxPoint &b)
Definition: Export.cpp:1288
ExportPlugin::GetFormat
virtual wxString GetFormat(int index)
Definition: Export.cpp:151
ExportPlugin::GetExtension
virtual FileExtension GetExtension(int index=0)
Return the (first) file name extension for the sub-format.
Definition: Export.cpp:161
Tags::Get
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:236
ExportPlugin::GetDescription
TranslatableString GetDescription(int index)
Definition: Export.cpp:156
ShuttleGui::Id
ShuttleGui & Id(int id)
Definition: ShuttleGui.cpp:2248
ThemeBase::Bitmap
wxBitmap & Bitmap(int iIndex)
Definition: Theme.cpp:1211
ProgressDialog::SetMessage
void SetMessage(const TranslatableString &message)
Definition: ProgressDialog.cpp:1510
ExportMixerPanel::mChannelHeight
int mChannelHeight
Definition: Export.h:285
Identifier
Definition: Types.h:64
AudacityExportMessageStr
TranslatableString AudacityExportMessageStr()
Definition: Export.cpp:1505
Exporter::mNumSelected
int mNumSelected
Definition: Export.h:248
eOkButton
@ eOkButton
Definition: ShuttleGui.h:607
Registry::Visitor
Definition: Registry.h:242
ExportMixerDialog
Dialog for advanced mixing.
Definition: Export.h:298
ExportPluginArray
std::vector< std::unique_ptr< ExportPlugin > > ExportPluginArray
Definition: Export.h:154
Registry::Visit
void Visit(Visitor &visitor, BaseItem *pTopItem, const GroupItem *pRegistry)
Definition: Registry.cpp:713
Exporter::OnFilterChanged
void OnFilterChanged(wxFileCtrlEvent &evt)
Definition: Export.cpp:1003
sampleFormat
sampleFormat
Definition: Types.h:717
ShowDiskFullExportErrorDialog
void ShowDiskFullExportErrorDialog(const wxFileNameWrapper &fileName)
Definition: Export.cpp:1529
Exporter::mSubFormat
int mSubFormat
Definition: Export.h:247
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1170
Exporter::mFormatName
FileExtension mFormatName
Definition: Export.h:232
ExportPlugin::SetExtensions
void SetExtensions(FileExtensions extensions, int index)
Definition: Export.cpp:131
factory
static RegisteredToolbarFactory factory
Definition: ControlToolBar.cpp:804
ExportPlugin::InitProgress
static void InitProgress(std::unique_ptr< ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
Definition: Export.cpp:250
Exporter::mMixerSpec
std::unique_ptr< MixerSpec > mMixerSpec
Definition: Export.h:236
anonymous_namespace{Export.cpp}::sRegistry
static Registry::GroupItem & sRegistry()
Definition: Export.cpp:285
ExportMixerPanel::mSelectedChannel
int mSelectedChannel
Definition: Export.h:283
MixerSpec::mMap
ArraysOf< bool > mMap
Definition: Mix.h:63
label
TranslatableString label
Definition: Tags.cpp:755
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1160
ProgressResult::Success
@ Success
Exporter::GetPlugins
const ExportPluginArray & GetPlugins()
Definition: Export.cpp:393
ExportPlugin::SetFormat
void SetFormat(const wxString &format, int index)
Definition: Export.cpp:116
FileDialog::GetPath
virtual wxString GetPath() const
Definition: gtk/FileDialogPrivate.cpp:514
ProjectSettings::GetShowId3Dialog
bool GetShowId3Dialog() const
Definition: ProjectSettings.h:72
ShuttleGuiBase::StartSimplebook
wxSimplebook * StartSimplebook()
Definition: ShuttleGui.cpp:1040
GetProjectFrame
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
Definition: Project.cpp:186
ExportMixerPanel
Panel that displays mixing for advanced mixing option.
Definition: Export.h:264
ExportMixerPanel::mTrackRects
ArrayOf< wxRect > mTrackRects
Definition: Export.h:282
eHelpButton
@ eHelpButton
Definition: ShuttleGui.h:611
Track::MonoChannel
static const auto MonoChannel
Definition: Track.h:267
FileDialog::SetFilterIndex
virtual void SetFilterIndex(int filterIndex)
Definition: gtk/FileDialogPrivate.cpp:599
Exporter::mPlugins
ExportPluginArray mPlugins
Definition: Export.h:238
FindProjectFrame
wxFrame * FindProjectFrame(AudacityProject *project)
Get a pointer to the window associated with a project, or null if the given pointer is null.
Definition: Project.h:172
ExportPlugin::DisplayOptions
virtual bool DisplayOptions(wxWindow *parent, int format=0)
Definition: Export.cpp:203
Exporter::mDialog
FileDialogWrapper * mDialog
Definition: Export.h:233
anonymous_namespace{Export.cpp}::sFactories
ExportPluginFactories & sFactories()
Definition: Export.cpp:302
Exporter::mT1
double mT1
Definition: Export.h:244
MixerSpec::GetNumTracks
unsigned GetNumTracks()
Definition: Mix.h:73
ExportMixerDialog::OnSize
void OnSize(wxSizeEvent &event)
Definition: Export.cpp:1466
ProgressResult::Stopped
@ Stopped
ExportPlugin::~ExportPlugin
virtual ~ExportPlugin()
Definition: Export.cpp:84
Track::IsSelected
bool IsSelected() const
Definition: Track.cpp:364
format
int format
Definition: ExportPCM.cpp:54
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:503
Export.h
Track::LeftChannel
static const auto LeftChannel
Definition: Track.h:265
ExportPlugin::SetDescription
void SetDescription(const TranslatableString &description, int index)
Definition: Export.cpp:121
Exporter::GetAutoExportFilterIndex
int GetAutoExportFilterIndex()
Definition: Export.cpp:1092
Exporter::mT0
double mT0
Definition: Export.h:243
PlayableTrack::GetNotSolo
bool GetNotSolo() const
Definition: Track.h:846
ShuttleGui::Size
ShuttleGui & Size(wxSize size)
Definition: ShuttleGui.h:725
Exporter::DisplayOptions
void DisplayOptions(int index)
Definition: Export.cpp:801
Tags::Set
static Tags & Set(AudacityProject &project, const std::shared_ptr< Tags > &tags)
Definition: Tags.cpp:246
AudacityExportCaptionStr
TranslatableString AudacityExportCaptionStr()
Definition: Export.cpp:1501
ShuttleGui::Prop
ShuttleGui & Prop(int iProp)
Definition: ShuttleGui.h:732
ShuttleGuiBase::AddSlider
wxSlider * AddSlider(const TranslatableString &Prompt, int pos, int Max, int Min=0)
Definition: ShuttleGui.cpp:582
FileDialogBase::SetUserPaneCreator
virtual void SetUserPaneCreator(UserPaneCreatorFunction creator, wxUIntPtr userdata)
Definition: FileDialog.cpp:30
ExportMixerDialog::GetMixerSpec
MixerSpec * GetMixerSpec()
Definition: Export.h:308
ExportPlugin::GetExtensions
virtual FileExtensions GetExtensions(int index=0)
Return all the file name extensions used for the sub-format.
Definition: Export.cpp:166
ShowErrorDialog
void ShowErrorDialog(wxWindow *parent, const TranslatableString &dlogTitle, const TranslatableString &message, const wxString &helpPage, const bool Close, const wxString &log)
Displays an error dialog with a button that offers help.
Definition: ErrorDialog.cpp:148
anonymous_namespace{Registry.cpp}::Path
std::vector< Identifier > Path
Definition: Registry.cpp:163
ShuttleGuiBase::StartStatic
wxStaticBox * StartStatic(const TranslatableString &Str, int iProp=0)
Definition: ShuttleGui.cpp:886
ExportPlugin::SetMaxChannels
void SetMaxChannels(unsigned maxchannels, unsigned index)
Definition: Export.cpp:141
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
ExportMixerPanel::OnPaint
void OnPaint(wxPaintEvent &event)
Definition: Export.cpp:1171
ProjectHistory::PushState
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
Definition: ProjectHistory.cpp:89
ShuttleGui::Name
ShuttleGui & Name(const TranslatableString &name)
Definition: ShuttleGui.h:670
wxDialogWrapper
Definition: wxPanelWrapper.h:81
Exporter::ExportTracks
bool ExportTracks()
Definition: Export.cpp:910
ExportMixerPanel::mHeight
int mHeight
Definition: Export.h:279
ExportMixerPanel::mWidth
int mWidth
Definition: Export.h:278
ShuttleGuiBase::AddWindow
wxWindow * AddWindow(wxWindow *pWindow)
Definition: ShuttleGui.cpp:292
ExportMixerDialog::~ExportMixerDialog
virtual ~ExportMixerDialog()
Definition: Export.cpp:1462
EnumSetting::ReadEnum
Enum ReadEnum() const
Definition: Prefs.h:193
HelpSystem::ShowHelp
static void ShowHelp(wxWindow *parent, const wxString &localFileName, const wxString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:238
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
ExportPlugin::GetMaxChannels
virtual unsigned GetMaxChannels(int index)
Definition: Export.cpp:179
ExportMixerDialog::OnSlider
void OnSlider(wxCommandEvent &event)
Definition: Export.cpp:1473
ShuttleGui::Position
ShuttleGui & Position(int flags)
Definition: ShuttleGui.h:719
FileDialogWrapper
Definition: wxPanelWrapper.h:165
_
#define _(s)
Definition: Internat.h:76
Exporter::mFileDialogTitle
TranslatableString mFileDialogTitle
Definition: Export.h:234
Exporter::mBook
wxSimplebook * mBook
Definition: Export.h:255
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:112
ExportPlugin::SetMask
void SetMask(FileNames::FileTypes mask, int index)
Definition: Export.cpp:136
Exporter::FindFormatIndex
int FindFormatIndex(int exportindex)
Definition: Export.cpp:379
ExportPlugin::CreateMixer
std::unique_ptr< Mixer > CreateMixer(const TrackList &tracks, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerSpec *mixerSpec)
Definition: Export.cpp:222
Verbatim
TranslatableString Verbatim(wxString str)
Definition: Types.h:581
ExportPlugin::SetCanMetaData
void SetCanMetaData(bool canmetadata, int index)
Definition: Export.cpp:146
ProjectWindow::Find
static ProjectWindow * Find(AudacityProject *pProject)
Definition: ProjectWindow.cpp:541
ExportPlugin::OptionsCreate
virtual void OptionsCreate(ShuttleGui &S, int format)=0
Definition: Export.cpp:208
ExportMixerPanel::mSelectedTrack
int mSelectedTrack
Definition: Export.h:283
Registry::GroupItem
Definition: Registry.h:126
ExportPlugin::AddFormat
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
Definition: Export.cpp:100
wxDialogWrapper::SetTitle
void SetTitle(const TranslatableString &title)
Definition: wxPanelWrapper.cpp:66
ExportPlugin::IsExtension
virtual bool IsExtension(const FileExtension &ext, int index)
Definition: Export.cpp:189
FileException::WriteFailureMessage
static TranslatableString WriteFailureMessage(const wxFileName &fileName)
Definition: FileException.cpp:61
ShuttleGui::AddStandardButtons
void AddStandardButtons(long buttons=eOkButton|eCancelButton, wxWindow *extra=NULL)
Definition: ShuttleGui.cpp:2406
ExportMixerPanel::IsOnLine
bool IsOnLine(wxPoint p, wxPoint la, wxPoint lb)
Definition: Export.cpp:1294
Exporter::mFilterIndex
int mFilterIndex
Definition: Export.h:245
FileNames::FileTypes
std::vector< FileType > FileTypes
Definition: FileNames.h:79
ExportMixerDialog::OnMixerPanelHelp
void OnMixerPanelHelp(wxCommandEvent &event)
Definition: Export.cpp:1495
theTheme
AUDACITY_DLL_API Theme theTheme
Definition: Theme.cpp:201
Exporter::mNumMono
unsigned mNumMono
Definition: Export.h:251
Registry::RegisterItem
void RegisterItem(GroupItem &registry, const Placement &placement, BaseItemPtr pItem)
Definition: Registry.cpp:750
ShuttleGuiBase::SetBorder
void SetBorder(int Border)
Definition: ShuttleGui.h:497
anonymous_namespace{Export.cpp}::ExportPluginFactories
std::vector< Exporter::ExportPluginFactory > ExportPluginFactories
Definition: Export.cpp:301
TrackList::Any
auto Any() -> TrackIterRange< TrackType >
Definition: Track.h:1354
ProgressDialog::Reinit
void Reinit()
Definition: ProgressDialog.cpp:1073
FileNames::FindDefaultPath
AUDACITY_DLL_API FilePath FindDefaultPath(Operation op)
MixerSpec
Class used with Mixer.
Definition: Mix.h:57
TranslatableString::Format
TranslatableString & Format(Args &&...args) &
Definition: Types.h:363
MixerSpec::GetNumChannels
unsigned GetNumChannels()
Definition: Mix.h:70
ShuttleGuiBase::AddVariableText
wxStaticText * AddVariableText(const TranslatableString &Str, bool bCenter=false, int PositionFlags=0, int wrapWidth=0)
Definition: ShuttleGui.cpp:456
FormatInfo
Definition: Export.h:43
FileDialog::SetFileExtension
virtual void SetFileExtension(const wxString &extension)
Definition: gtk/FileDialogPrivate.cpp:474
ShuttleGuiBase::EndStatic
void EndStatic()
Definition: ShuttleGui.cpp:915
safenew
#define safenew
Definition: MemoryX.h:8
settings
static Settings & settings()
Definition: TrackInfo.cpp:87
Exporter::ExamineTracks
bool ExamineTracks()
Definition: Export.cpp:509
AColor::playRegionBrush
static wxBrush playRegionBrush[2]
Definition: AColor.h:98
Exporter::GetAutoExportSubFormat
int GetAutoExportSubFormat()
Definition: Export.cpp:1088
AColor::envelopeBrush
static wxBrush envelopeBrush
Definition: AColor.h:107
Exporter::ProcessFromTimerRecording
bool ProcessFromTimerRecording(bool selectedOnly, double t0, double t1, wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex)
Definition: Export.cpp:1041
FileDialog::ShowModal
virtual int ShowModal()
Definition: gtk/FileDialogPrivate.cpp:445
Exporter::DoEditMetadata
static bool DoEditMetadata(AudacityProject &project, const TranslatableString &title, const TranslatableString &shortUndoDescription, bool force)
Definition: Export.cpp:398
AudacityProject::GetProjectName
const wxString & GetProjectName() const
Definition: Project.cpp:163
END_EVENT_TABLE
END_EVENT_TABLE()
Exporter::ExportPluginFactory
std::function< std::unique_ptr< ExportPlugin >() > ExportPluginFactory
Definition: Export.h:169
FileDialog::GetFilterIndex
virtual int GetFilterIndex() const
Definition: gtk/FileDialogPrivate.cpp:604
Exporter::CheckMix
bool CheckMix(bool prompt=true)
Definition: Export.cpp:831
ProjectHistory::Get
static ProjectHistory & Get(AudacityProject &project)
Definition: ProjectHistory.cpp:26
Exporter::mNumRight
unsigned mNumRight
Definition: Export.h:250
Exporter::RegisteredExportPlugin::RegisteredExportPlugin
RegisteredExportPlugin(const Identifier &id, const ExportPluginFactory &, const Registry::Placement &placement={ wxEmptyString, {} })
Definition: Export.cpp:309
ID_SLIDER_CHANNEL
@ ID_SLIDER_CHANNEL
Definition: Export.cpp:1362
anonymous_namespace{Export.cpp}::PathStart
const auto PathStart
Definition: Export.cpp:283
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:638
Registry::OrderingPreferenceInitializer
Definition: Registry.h:273
ExportPlugin::CheckFileName
virtual bool CheckFileName(wxFileName &filename, int format=0)
Definition: Export.cpp:88
Exporter::mNumLeft
unsigned mNumLeft
Definition: Export.h:249
ExportMixerDialog::mMixerSpec
std::unique_ptr< MixerSpec > mMixerSpec
Definition: Export.h:312
Exporter::mProject
AudacityProject * mProject
Definition: Export.h:235