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