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