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