1 /**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ExportFFmpegDialogs.cpp
6
7 Audacity(R) is copyright (c) 1999-2010 Audacity Team.
8 License: GPL v2. See License.txt.
9
10 LRN
11
12 ******************************************************************//**
13
14 \class ExportFFmpegAC3Options
15 \brief Options dialog for FFmpeg exporting of AC3 format.
16
17 *//***************************************************************//**
18
19 \class ExportFFmpegAACOptions
20 \brief Options dialog for FFmpeg exporting of AAC format.
21
22 *//***************************************************************//**
23
24 \class ExportFFmpegAMRNBOptions
25 \brief Options dialog for FFmpeg exporting of AMRNB format.
26
27 *//***************************************************************//**
28
29 \class ExportFFmpegOPUSOptions
30 \brief Options dialog for FFmpeg exporting of OPUS format.
31
32 *//***************************************************************//**
33
34 \class ExportFFmpegWMAOptions
35 \brief Options dialog for FFmpeg exporting of WMA format.
36
37 *//***************************************************************//**
38
39 \class ExportFFmpegOptions
40 \brief Options dialog for Custom FFmpeg export format.
41
42 *//*******************************************************************/
43
44
45 #include "ExportFFmpegDialogs.h"
46
47 #include "../FFmpeg.h"
48 #include "FFmpegFunctions.h"
49
50 #include <wx/app.h>
51 #include <wx/checkbox.h>
52 #include <wx/choice.h>
53 #include <wx/intl.h>
54 #include <wx/timer.h>
55 #include <wx/string.h>
56 #include <wx/textctrl.h>
57 #include <wx/listbox.h>
58 #include <wx/window.h>
59 #include <wx/spinctrl.h>
60 #include <wx/combobox.h>
61 #include <wx/stattext.h>
62
63 #include "../widgets/FileDialog/FileDialog.h"
64
65 #include "../Mix.h"
66 #include "../Tags.h"
67 #include "../widgets/AudacityMessageBox.h"
68 #include "../widgets/HelpSystem.h"
69
70 #include "Export.h"
71 #include "FFmpeg.h"
72
73 #if defined(USE_FFMPEG)
74
75 /// This construction defines a enumeration of UI element IDs, and a static
76 /// array of their string representations (this way they're always synchronized).
77 /// Do not store the enumerated values in external files, as they may change;
78 /// the strings may be stored.
79 #define FFMPEG_EXPORT_CTRL_ID_ENTRIES \
80 FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY(FEFirstID, 20000), \
81 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEFormatID), \
82 FFMPEG_EXPORT_CTRL_ID_ENTRY(FECodecID), \
83 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEBitrateID), \
84 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEQualityID), \
85 FFMPEG_EXPORT_CTRL_ID_ENTRY(FESampleRateID), \
86 FFMPEG_EXPORT_CTRL_ID_ENTRY(FELanguageID), \
87 FFMPEG_EXPORT_CTRL_ID_ENTRY(FETagID), \
88 FFMPEG_EXPORT_CTRL_ID_ENTRY(FECutoffID), \
89 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEFrameSizeID), \
90 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEBufSizeID), \
91 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEProfileID), \
92 FFMPEG_EXPORT_CTRL_ID_ENTRY(FECompLevelID), \
93 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEUseLPCID), \
94 FFMPEG_EXPORT_CTRL_ID_ENTRY(FELPCCoeffsID), \
95 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMinPredID), \
96 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMaxPredID), \
97 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEPredOrderID), \
98 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMinPartOrderID), \
99 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMaxPartOrderID), \
100 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEMuxRateID), \
101 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEPacketSizeID), \
102 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEBitReservoirID), \
103 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEVariableBlockLenID), \
104 FFMPEG_EXPORT_CTRL_ID_ENTRY(FELastID), \
105 \
106 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEFormatLabelID), \
107 FFMPEG_EXPORT_CTRL_ID_ENTRY(FECodecLabelID), \
108 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEFormatNameID), \
109 FFMPEG_EXPORT_CTRL_ID_ENTRY(FECodecNameID), \
110 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEPresetID), \
111 FFMPEG_EXPORT_CTRL_ID_ENTRY(FESavePresetID), \
112 FFMPEG_EXPORT_CTRL_ID_ENTRY(FELoadPresetID), \
113 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEDeletePresetID), \
114 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEAllFormatsID), \
115 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEAllCodecsID), \
116 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEImportPresetsID), \
117 FFMPEG_EXPORT_CTRL_ID_ENTRY(FEExportPresetsID) \
118
119 // First the enumeration
120 #define FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY(name, num) name = num
121 #define FFMPEG_EXPORT_CTRL_ID_ENTRY(name) name
122
123 enum FFmpegExportCtrlID {
124 FFMPEG_EXPORT_CTRL_ID_ENTRIES
125 };
126
127 // Now the string representations
128 #undef FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY
129 #define FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY(name, num) wxT(#name)
130 #undef FFMPEG_EXPORT_CTRL_ID_ENTRY
131 #define FFMPEG_EXPORT_CTRL_ID_ENTRY(name) wxT(#name)
132 static const wxChar *FFmpegExportCtrlIDNames[] = {
133 FFMPEG_EXPORT_CTRL_ID_ENTRIES
134 };
135
136 #undef FFMPEG_EXPORT_CTRL_ID_ENTRIES
137 #undef FFMPEG_EXPORT_CTRL_ID_ENTRY
138 #undef FFMPEG_EXPORT_CTRL_ID_FIRST_ENTRY
139
140 //----------------------------------------------------------------------------
141 // ExportFFmpegAC3Options Class
142 //----------------------------------------------------------------------------
143
144 namespace
145 {
146
147 // i18n-hint kbps abbreviates "thousands of bits per second"
n_kbps(int n)148 inline TranslatableString n_kbps(int n) { return XO("%d kbps").Format( n ); }
149
150 const TranslatableStrings AC3BitRateNames{
151 n_kbps( 32 ),
152 n_kbps( 40 ),
153 n_kbps( 48 ),
154 n_kbps( 56 ),
155 n_kbps( 64 ),
156 n_kbps( 80 ),
157 n_kbps( 96 ),
158 n_kbps( 112 ),
159 n_kbps( 128 ),
160 n_kbps( 160 ),
161 n_kbps( 192 ),
162 n_kbps( 224 ),
163 n_kbps( 256 ),
164 n_kbps( 320 ),
165 n_kbps( 384 ),
166 n_kbps( 448 ),
167 n_kbps( 512 ),
168 n_kbps( 576 ),
169 n_kbps( 640 ),
170 };
171
172 const std::vector< int > AC3BitRateValues{
173 32000,
174 40000,
175 48000,
176 56000,
177 64000,
178 80000,
179 96000,
180 112000,
181 128000,
182 160000,
183 192000,
184 224000,
185 256000,
186 320000,
187 384000,
188 448000,
189 512000,
190 576000,
191 640000,
192 };
193
194 }
195
196 const int ExportFFmpegAC3Options::iAC3SampleRates[] = { 32000, 44100, 48000, 0 };
197
ExportFFmpegAC3Options(wxWindow * parent,int WXUNUSED (format))198 ExportFFmpegAC3Options::ExportFFmpegAC3Options(wxWindow *parent, int WXUNUSED(format))
199 : wxPanelWrapper(parent, wxID_ANY)
200 {
201 ShuttleGui S(this, eIsCreatingFromPrefs);
202 PopulateOrExchange(S);
203
204 TransferDataToWindow();
205 }
206
~ExportFFmpegAC3Options()207 ExportFFmpegAC3Options::~ExportFFmpegAC3Options()
208 {
209 TransferDataFromWindow();
210 }
211
212 ///
213 ///
PopulateOrExchange(ShuttleGui & S)214 void ExportFFmpegAC3Options::PopulateOrExchange(ShuttleGui & S)
215 {
216 S.StartVerticalLay();
217 {
218 S.StartHorizontalLay(wxCENTER);
219 {
220 S.StartMultiColumn(2, wxCENTER);
221 {
222 S.TieNumberAsChoice(
223 XXO("Bit Rate:"),
224 {wxT("/FileFormats/AC3BitRate"),
225 160000},
226 AC3BitRateNames,
227 &AC3BitRateValues
228 );
229 }
230 S.EndMultiColumn();
231 }
232 S.EndHorizontalLay();
233 }
234 S.EndVerticalLay();
235 }
236
237 ///
238 ///
TransferDataToWindow()239 bool ExportFFmpegAC3Options::TransferDataToWindow()
240 {
241 return true;
242 }
243
244 ///
245 ///
TransferDataFromWindow()246 bool ExportFFmpegAC3Options::TransferDataFromWindow()
247 {
248 ShuttleGui S(this, eIsSavingToPrefs);
249 PopulateOrExchange(S);
250
251 gPrefs->Flush();
252
253 return true;
254 }
255
256 //----------------------------------------------------------------------------
257 // ExportFFmpegAACOptions Class
258 //----------------------------------------------------------------------------
259
ExportFFmpegAACOptions(wxWindow * parent,int WXUNUSED (format))260 ExportFFmpegAACOptions::ExportFFmpegAACOptions(wxWindow *parent, int WXUNUSED(format))
261 : wxPanelWrapper(parent, wxID_ANY)
262 {
263 ShuttleGui S(this, eIsCreatingFromPrefs);
264 PopulateOrExchange(S);
265
266 TransferDataToWindow();
267 }
268
~ExportFFmpegAACOptions()269 ExportFFmpegAACOptions::~ExportFFmpegAACOptions()
270 {
271 TransferDataFromWindow();
272 }
273
274 ///
275 ///
PopulateOrExchange(ShuttleGui & S)276 void ExportFFmpegAACOptions::PopulateOrExchange(ShuttleGui & S)
277 {
278 S.StartVerticalLay();
279 {
280 S.StartHorizontalLay(wxEXPAND);
281 {
282 S.SetSizerProportion(1);
283 S.StartMultiColumn(2, wxCENTER);
284 {
285 S.SetStretchyCol(1);
286 S.Prop(1).TieSlider(
287 XXO("Quality (kbps):"),
288 {wxT("/FileFormats/AACQuality"), 160},320, 98);
289 }
290 S.EndMultiColumn();
291 }
292 S.EndHorizontalLay();
293 }
294 S.EndVerticalLay();
295 }
296
297 ///
298 ///
TransferDataToWindow()299 bool ExportFFmpegAACOptions::TransferDataToWindow()
300 {
301 return true;
302 }
303
304 ///
305 ///
TransferDataFromWindow()306 bool ExportFFmpegAACOptions::TransferDataFromWindow()
307 {
308 ShuttleGui S(this, eIsSavingToPrefs);
309 PopulateOrExchange(S);
310
311 gPrefs->Flush();
312
313 return true;
314 }
315
316 //----------------------------------------------------------------------------
317 // ExportFFmpegAMRNBOptions Class
318 //----------------------------------------------------------------------------
319
320 namespace {
321
322 // i18n-hint kbps abbreviates "thousands of bits per second"
f_kbps(double d)323 inline TranslatableString f_kbps( double d ) { return XO("%.2f kbps").Format( d ); }
324
325 /// Bit Rates supported by libAMR-NB encoder
326 /// Sample Rate is always 8 kHz
327 const TranslatableStrings AMRNBBitRateNames
328 {
329 f_kbps( 4.75 ),
330 f_kbps( 5.15 ),
331 f_kbps( 5.90 ),
332 f_kbps( 6.70 ),
333 f_kbps( 7.40 ),
334 f_kbps( 7.95 ),
335 f_kbps( 10.20 ),
336 f_kbps( 12.20 ),
337 };
338
339 const std::vector< int > AMRNBBitRateValues
340 {
341 4750,
342 5150,
343 5900,
344 6700,
345 7400,
346 7950,
347 10200,
348 12200,
349 };
350
351 }
352
ExportFFmpegAMRNBOptions(wxWindow * parent,int WXUNUSED (format))353 ExportFFmpegAMRNBOptions::ExportFFmpegAMRNBOptions(wxWindow *parent, int WXUNUSED(format))
354 : wxPanelWrapper(parent, wxID_ANY)
355 {
356 ShuttleGui S(this, eIsCreatingFromPrefs);
357 PopulateOrExchange(S);
358
359 TransferDataToWindow();
360 }
361
~ExportFFmpegAMRNBOptions()362 ExportFFmpegAMRNBOptions::~ExportFFmpegAMRNBOptions()
363 {
364 TransferDataFromWindow();
365 }
366
367 ///
368 ///
PopulateOrExchange(ShuttleGui & S)369 void ExportFFmpegAMRNBOptions::PopulateOrExchange(ShuttleGui & S)
370 {
371 S.StartVerticalLay();
372 {
373 S.StartHorizontalLay(wxCENTER);
374 {
375 S.StartMultiColumn(2, wxCENTER);
376 {
377 S.TieNumberAsChoice(
378 XXO("Bit Rate:"),
379 {wxT("/FileFormats/AMRNBBitRate"),
380 12200},
381 AMRNBBitRateNames,
382 &AMRNBBitRateValues
383 );
384 }
385 S.EndMultiColumn();
386 }
387 S.EndHorizontalLay();
388 }
389 S.EndVerticalLay();
390 }
391
392 ///
393 ///
TransferDataToWindow()394 bool ExportFFmpegAMRNBOptions::TransferDataToWindow()
395 {
396 return true;
397 }
398
399 ///
400 ///
TransferDataFromWindow()401 bool ExportFFmpegAMRNBOptions::TransferDataFromWindow()
402 {
403 ShuttleGui S(this, eIsSavingToPrefs);
404 PopulateOrExchange(S);
405
406 gPrefs->Flush();
407
408 return true;
409 }
410
411 //----------------------------------------------------------------------------
412 // ExportFFmpegOPUSOptions Class
413 //----------------------------------------------------------------------------
414
415 namespace {
416
417 /// Bit Rates supported by OPUS encoder. Setting bit rate to other values will not result in different file size.
418 ChoiceSetting OPUSBitrate
419 {
420 wxT("/FileFormats/OPUSBitrate"),
421 {
422 ByColumns,
423 {
424 n_kbps( 6 ),
425 n_kbps( 8 ),
426 n_kbps( 16 ),
427 n_kbps( 24 ),
428 n_kbps( 32 ),
429 n_kbps( 40 ),
430 n_kbps( 48 ),
431 n_kbps( 64 ),
432 n_kbps( 80 ),
433 n_kbps( 96 ),
434 n_kbps( 128 ),
435 n_kbps( 160 ),
436 n_kbps( 192 ),
437 n_kbps( 256 ),
438 },
439 {
440 wxT("6000"),
441 wxT("8000"),
442 wxT("16000"),
443 wxT("24000"),
444 wxT("32000"),
445 wxT("40000"),
446 wxT("48000"),
447 wxT("64000"),
448 wxT("80000"),
449 wxT("96000"),
450 wxT("128000"),
451 wxT("160000"),
452 wxT("192000"),
453 wxT("256000"),
454 }
455 },
456 7 // "128 kbps"
457 };
458
459 ChoiceSetting OPUSCompression
460 {
461 wxT("/FileFormats/OPUSCompression"),
462 {
463 ByColumns,
464 {
465 XO("0"),
466 XO("1"),
467 XO("2"),
468 XO("3"),
469 XO("4"),
470 XO("5"),
471 XO("6"),
472 XO("7"),
473 XO("8"),
474 XO("9"),
475 XO("10"),
476 },
477 {
478 wxT("0"),
479 wxT("1"),
480 wxT("2"),
481 wxT("3"),
482 wxT("4"),
483 wxT("5"),
484 wxT("6"),
485 wxT("7"),
486 wxT("8"),
487 wxT("9"),
488 wxT("10"),
489 }
490 },
491 10 // "10"
492 };
493
494
495 ChoiceSetting OPUSVbrMode
496 {
497 wxT("/FileFormats/OPUSVbrMode"),
498 {
499 ByColumns,
500 {
501 XO("Off"),
502 XO("On"),
503 XO("Constrained"),
504 },
505 {
506 wxT("off"),
507 wxT("on"),
508 wxT("constrained"),
509 }
510 },
511 1 // "On"
512 };
513
514 ChoiceSetting OPUSApplication
515 {
516 wxT("/FileFormats/OPUSApplication"),
517 {
518 ByColumns,
519 {
520 XO("VOIP"),
521 XO("Audio"),
522 XO("Low Delay"),
523 },
524 {
525 wxT("voip"),
526 wxT("audio"),
527 wxT("lowdelay"),
528 }
529 },
530 1 // "Audio"
531 };
532
533 ChoiceSetting OPUSFrameDuration
534 {
535 wxT("/FileFormats/OPUSFrameDuration"),
536 {
537 ByColumns,
538 {
539 XO("2.5 ms"),
540 XO("5 ms"),
541 XO("10 ms"),
542 XO("20 ms"),
543 XO("40 ms"),
544 XO("60 ms"),
545 },
546 {
547 wxT("2.5"),
548 wxT("5"),
549 wxT("10"),
550 wxT("20"),
551 wxT("40"),
552 wxT("60"),
553 }
554 },
555 3 // "20"
556 };
557
558 ChoiceSetting OPUSCutoff
559 {
560 wxT("/FileFormats/OPUSCutoff"),
561 {
562 ByColumns,
563 {
564 XO("Disabled"),
565 XO("Narrowband"),
566 XO("Mediumband"),
567 XO("Wideband"),
568 XO("Super Wideband"),
569 XO("Fullband"),
570 },
571 {
572 wxT("0"),
573 wxT("4000"),
574 wxT("6000"),
575 wxT("8000"),
576 wxT("12000"),
577 wxT("20000"),
578 }
579 },
580 0 // "Disabled"
581 };
582 }
583
ExportFFmpegOPUSOptions(wxWindow * parent,int WXUNUSED (format))584 ExportFFmpegOPUSOptions::ExportFFmpegOPUSOptions(wxWindow *parent, int WXUNUSED(format))
585 : wxPanelWrapper(parent, wxID_ANY)
586 {
587 ShuttleGui S(this, eIsCreatingFromPrefs);
588 PopulateOrExchange(S);
589
590 TransferDataToWindow();
591 }
592
~ExportFFmpegOPUSOptions()593 ExportFFmpegOPUSOptions::~ExportFFmpegOPUSOptions()
594 {
595 TransferDataFromWindow();
596 }
597
598 ///
599 ///
PopulateOrExchange(ShuttleGui & S)600 void ExportFFmpegOPUSOptions::PopulateOrExchange(ShuttleGui & S)
601 {
602 S.SetSizerProportion(1);
603 S.SetBorder(4);
604 S.StartVerticalLay();
605 {
606 S.StartHorizontalLay(wxCENTER);
607 {
608 S.StartMultiColumn(2, wxCENTER);
609 {
610 S.StartMultiColumn(2, wxCENTER);
611 {
612 S.TieChoice(
613 XXO("Bit Rate:"),
614 OPUSBitrate);
615
616 S.TieChoice(
617 XXO("Compression"),
618 OPUSCompression);
619
620 S.TieChoice(
621 XXO("Frame Duration:"),
622 OPUSFrameDuration);
623 }
624 S.EndMultiColumn();
625
626 S.StartMultiColumn(2, wxCENTER);
627 {
628 S.TieChoice(
629 XXO("Vbr Mode:"),
630 OPUSVbrMode);
631
632 S.TieChoice(
633 XXO("Application:"),
634 OPUSApplication);
635
636 S.TieChoice(
637 XXO("Cutoff:"),
638 OPUSCutoff);
639
640 }
641 S.EndMultiColumn();
642 }
643 S.EndMultiColumn();
644 }
645 S.EndHorizontalLay();
646 }
647 S.EndVerticalLay();
648 }
649
650 ///
651 ///
TransferDataToWindow()652 bool ExportFFmpegOPUSOptions::TransferDataToWindow()
653 {
654 return true;
655 }
656
657 ///
658 ///
TransferDataFromWindow()659 bool ExportFFmpegOPUSOptions::TransferDataFromWindow()
660 {
661 ShuttleGui S(this, eIsSavingToPrefs);
662 PopulateOrExchange(S);
663
664 gPrefs->Flush();
665
666 return true;
667 }
668
669 //----------------------------------------------------------------------------
670 // ExportFFmpegWMAOptions Class
671 //----------------------------------------------------------------------------
672
673 const int ExportFFmpegWMAOptions::iWMASampleRates[] =
674 { 8000, 11025, 16000, 22050, 44100, 0};
675
676 namespace {
677
678 /// Bit Rates supported by WMA encoder. Setting bit rate to other values will not result in different file size.
679 const TranslatableStrings WMABitRateNames
680 {
681 n_kbps(24),
682 n_kbps(32),
683 n_kbps(40),
684 n_kbps(48),
685 n_kbps(64),
686 n_kbps(80),
687 n_kbps(96),
688 n_kbps(128),
689 n_kbps(160),
690 n_kbps(192),
691 n_kbps(256),
692 n_kbps(320),
693 };
694
695 const std::vector< int > WMABitRateValues{
696 24000,
697 32000,
698 40000,
699 48000,
700 64000,
701 80000,
702 96000,
703 128000,
704 160000,
705 192000,
706 256000,
707 320000,
708 };
709
710 }
711
ExportFFmpegWMAOptions(wxWindow * parent,int WXUNUSED (format))712 ExportFFmpegWMAOptions::ExportFFmpegWMAOptions(wxWindow *parent, int WXUNUSED(format))
713 : wxPanelWrapper(parent, wxID_ANY)
714 {
715 ShuttleGui S(this, eIsCreatingFromPrefs);
716 PopulateOrExchange(S);
717
718 TransferDataToWindow();
719 }
720
~ExportFFmpegWMAOptions()721 ExportFFmpegWMAOptions::~ExportFFmpegWMAOptions()
722 {
723 TransferDataFromWindow();
724 }
725
726 ///
727 ///
PopulateOrExchange(ShuttleGui & S)728 void ExportFFmpegWMAOptions::PopulateOrExchange(ShuttleGui & S)
729 {
730 S.StartVerticalLay();
731 {
732 S.StartHorizontalLay(wxCENTER);
733 {
734 S.StartMultiColumn(2, wxCENTER);
735 {
736 S.TieNumberAsChoice(
737 XXO("Bit Rate:"),
738 {wxT("/FileFormats/WMABitRate"),
739 128000},
740 WMABitRateNames,
741 &WMABitRateValues
742 );
743 }
744 S.EndMultiColumn();
745 }
746 S.EndHorizontalLay();
747 }
748 S.EndVerticalLay();
749 }
750
751 ///
752 ///
TransferDataToWindow()753 bool ExportFFmpegWMAOptions::TransferDataToWindow()
754 {
755 return true;
756 }
757
758 ///
759 ///
TransferDataFromWindow()760 bool ExportFFmpegWMAOptions::TransferDataFromWindow()
761 {
762 ShuttleGui S(this, eIsSavingToPrefs);
763 PopulateOrExchange(S);
764
765 gPrefs->Flush();
766
767 return true;
768 }
769
770 //----------------------------------------------------------------------------
771 // ExportFFmpegCustomOptions Class
772 //----------------------------------------------------------------------------
773
774 #define OpenID 9000
775
BEGIN_EVENT_TABLE(ExportFFmpegCustomOptions,wxPanelWrapper)776 BEGIN_EVENT_TABLE(ExportFFmpegCustomOptions, wxPanelWrapper)
777 EVT_BUTTON(OpenID, ExportFFmpegCustomOptions::OnOpen)
778 END_EVENT_TABLE()
779
780 ExportFFmpegCustomOptions::ExportFFmpegCustomOptions(wxWindow *parent, int WXUNUSED(format))
781 : wxPanelWrapper(parent, wxID_ANY),
782 mFormat(NULL),
783 mCodec(NULL)
784 {
785 ShuttleGui S(this, eIsCreatingFromPrefs);
786 PopulateOrExchange(S);
787
788 TransferDataToWindow();
789 }
790
~ExportFFmpegCustomOptions()791 ExportFFmpegCustomOptions::~ExportFFmpegCustomOptions()
792 {
793 TransferDataFromWindow();
794 }
795
796 ///
797 ///
PopulateOrExchange(ShuttleGui & S)798 void ExportFFmpegCustomOptions::PopulateOrExchange(ShuttleGui & S)
799 {
800 S.StartHorizontalLay(wxCENTER);
801 {
802 S.StartVerticalLay(wxCENTER, 0);
803 {
804 S.Id(OpenID).AddButton(XXO("Open custom FFmpeg format options"));
805 S.StartMultiColumn(2, wxCENTER);
806 {
807 S.AddPrompt(XXO("Current Format:"));
808 mFormat = S.Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
809 S.AddPrompt(XXO("Current Codec:"));
810 mCodec = S.Style(wxTE_READONLY).AddTextBox({}, wxT(""), 25);
811 }
812 S.EndMultiColumn();
813 }
814 S.EndHorizontalLay();
815 }
816 S.EndHorizontalLay();
817 }
818
819 ///
820 ///
TransferDataToWindow()821 bool ExportFFmpegCustomOptions::TransferDataToWindow()
822 {
823 if (mFormat)
824 {
825 mFormat->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegFormat"), wxT("")));
826 mCodec->SetValue(gPrefs->Read(wxT("/FileFormats/FFmpegCodec"), wxT("")));
827 }
828 return true;
829 }
830
831 ///
832 ///
TransferDataFromWindow()833 bool ExportFFmpegCustomOptions::TransferDataFromWindow()
834 {
835 return true;
836 }
837
838 ///
839 ///
OnOpen(wxCommandEvent & WXUNUSED (evt))840 void ExportFFmpegCustomOptions::OnOpen(wxCommandEvent & WXUNUSED(evt))
841 {
842 // Show "Locate FFmpeg" dialog
843 auto ffmpeg = FFmpegFunctions::Load();
844 if (!ffmpeg)
845 {
846 FindFFmpegLibs();
847 if (!LoadFFmpeg(true))
848 {
849 return;
850 }
851 }
852
853 #ifdef __WXMAC__
854 // Bug 2077 Must be a parent window on OSX or we will appear behind.
855 auto pWin = wxGetTopLevelParent( this );
856 #else
857 // Use GetTopWindow on windows as there is no hWnd with top level parent.
858 auto pWin = wxTheApp->GetTopWindow();
859 #endif
860
861 ExportFFmpegOptions od(pWin);
862 od.ShowModal();
863
864 TransferDataToWindow();
865 }
866
FFmpegPreset()867 FFmpegPreset::FFmpegPreset()
868 {
869 mControlState.resize(FELastID - FEFirstID);
870 }
871
~FFmpegPreset()872 FFmpegPreset::~FFmpegPreset()
873 {
874 }
875
FFmpegPresets()876 FFmpegPresets::FFmpegPresets()
877 {
878 mPreset = NULL;
879 mAbortImport = false;
880
881 XMLFileReader xmlfile;
882 wxFileName xmlFileName(FileNames::DataDir(), wxT("ffmpeg_presets.xml"));
883 xmlfile.Parse(this,xmlFileName.GetFullPath());
884 }
885
~FFmpegPresets()886 FFmpegPresets::~FFmpegPresets()
887 {
888 // We're in a destructor! Don't let exceptions out!
889 GuardedCall( [&] {
890 wxFileName xmlFileName{ FileNames::DataDir(), wxT("ffmpeg_presets.xml") };
891 XMLFileWriter writer{
892 xmlFileName.GetFullPath(), XO("Error Saving FFmpeg Presets") };
893 WriteXMLHeader(writer);
894 WriteXML(writer);
895 writer.Commit();
896 } );
897 }
898
ImportPresets(wxString & filename)899 void FFmpegPresets::ImportPresets(wxString &filename)
900 {
901 mPreset = NULL;
902 mAbortImport = false;
903
904 FFmpegPresetMap savePresets = mPresets;
905
906 XMLFileReader xmlfile;
907 bool success = xmlfile.Parse(this,filename);
908 if (!success || mAbortImport) {
909 mPresets = savePresets;
910 }
911 }
912
ExportPresets(wxString & filename)913 void FFmpegPresets::ExportPresets(wxString &filename)
914 {
915 GuardedCall( [&] {
916 XMLFileWriter writer{ filename, XO("Error Saving FFmpeg Presets") };
917 WriteXMLHeader(writer);
918 WriteXML(writer);
919 writer.Commit();
920 } );
921 }
922
GetPresetList(wxArrayString & list)923 void FFmpegPresets::GetPresetList(wxArrayString &list)
924 {
925 list.clear();
926 FFmpegPresetMap::iterator iter;
927 for (iter = mPresets.begin(); iter != mPresets.end(); ++iter)
928 {
929 list.push_back(iter->second.mPresetName);
930 }
931
932 std::sort( list.begin(), list.end() );
933 }
934
DeletePreset(wxString & name)935 void FFmpegPresets::DeletePreset(wxString &name)
936 {
937 FFmpegPresetMap::iterator iter = mPresets.find(name);
938 if (iter != mPresets.end())
939 {
940 mPresets.erase(iter);
941 }
942 }
943
FindPreset(wxString & name)944 FFmpegPreset *FFmpegPresets::FindPreset(wxString &name)
945 {
946 FFmpegPresetMap::iterator iter = mPresets.find(name);
947 if (iter != mPresets.end())
948 {
949 return &iter->second;
950 }
951
952 return NULL;
953 }
954
955 // return false if overwrite was not allowed.
OverwriteIsOk(wxString & name)956 bool FFmpegPresets::OverwriteIsOk( wxString &name )
957 {
958 FFmpegPreset *preset = FindPreset(name);
959 if (preset)
960 {
961 auto query = XO("Overwrite preset '%s'?").Format(name);
962 int action = AudacityMessageBox(
963 query,
964 XO("Confirm Overwrite"),
965 wxYES_NO | wxCENTRE);
966 if (action == wxNO) return false;
967 }
968 return true;
969 }
970
971
SavePreset(ExportFFmpegOptions * parent,wxString & name)972 bool FFmpegPresets::SavePreset(ExportFFmpegOptions *parent, wxString &name)
973 {
974 wxString format;
975 wxString codec;
976 FFmpegPreset *preset;
977
978 {
979 wxWindow *wnd;
980 wxListBox *lb;
981
982 wnd = dynamic_cast<wxWindow*>(parent)->FindWindowById(FEFormatID,parent);
983 lb = dynamic_cast<wxListBox*>(wnd);
984 if (lb->GetSelection() < 0)
985 {
986 AudacityMessageBox( XO("Please select format before saving a profile") );
987 return false;
988 }
989 format = lb->GetStringSelection();
990
991 wnd = dynamic_cast<wxWindow*>(parent)->FindWindowById(FECodecID,parent);
992 lb = dynamic_cast<wxListBox*>(wnd);
993 if (lb->GetSelection() < 0)
994 {
995 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
996 AudacityMessageBox( XO("Please select codec before saving a profile") );
997 return false;
998 }
999 codec = lb->GetStringSelection();
1000 }
1001
1002 preset = &mPresets[name];
1003 preset->mPresetName = name;
1004
1005 wxSpinCtrl *sc;
1006 wxTextCtrl *tc;
1007 wxCheckBox *cb;
1008 wxChoice *ch;
1009
1010 for (int id = FEFirstID; id < FELastID; id++)
1011 {
1012 wxWindow *wnd = dynamic_cast<wxWindow*>(parent)->FindWindowById(id,parent);
1013 if (wnd != NULL)
1014 {
1015 switch(id)
1016 {
1017 case FEFormatID:
1018 preset->mControlState[id - FEFirstID] = format;
1019 break;
1020 case FECodecID:
1021 preset->mControlState[id - FEFirstID] = codec;
1022 break;
1023 // Spin control
1024 case FEBitrateID:
1025 case FEQualityID:
1026 case FESampleRateID:
1027 case FECutoffID:
1028 case FEFrameSizeID:
1029 case FEBufSizeID:
1030 case FECompLevelID:
1031 case FELPCCoeffsID:
1032 case FEMinPredID:
1033 case FEMaxPredID:
1034 case FEMinPartOrderID:
1035 case FEMaxPartOrderID:
1036 case FEMuxRateID:
1037 case FEPacketSizeID:
1038 sc = dynamic_cast<wxSpinCtrl*>(wnd);
1039 preset->mControlState[id - FEFirstID] = wxString::Format(wxT("%d"),sc->GetValue());
1040 break;
1041 // Text control
1042 case FELanguageID:
1043 case FETagID:
1044 tc = dynamic_cast<wxTextCtrl*>(wnd);
1045 preset->mControlState[id - FEFirstID] = tc->GetValue();
1046 break;
1047 // Choice
1048 case FEProfileID:
1049 case FEPredOrderID:
1050 ch = dynamic_cast<wxChoice*>(wnd);
1051 preset->mControlState[id - FEFirstID] = wxString::Format(wxT("%d"),ch->GetSelection());
1052 break;
1053 // Check box
1054 case FEUseLPCID:
1055 case FEBitReservoirID:
1056 case FEVariableBlockLenID:
1057 cb = dynamic_cast<wxCheckBox*>(wnd);
1058 preset->mControlState[id - FEFirstID] = wxString::Format(wxT("%d"),cb->GetValue());
1059 break;
1060 }
1061 }
1062 }
1063 return true;
1064 }
1065
LoadPreset(ExportFFmpegOptions * parent,wxString & name)1066 void FFmpegPresets::LoadPreset(ExportFFmpegOptions *parent, wxString &name)
1067 {
1068 FFmpegPreset *preset = FindPreset(name);
1069 if (!preset)
1070 {
1071 AudacityMessageBox( XO("Preset '%s' does not exist." ).Format(name));
1072 return;
1073 }
1074
1075 wxListBox *lb;
1076 wxSpinCtrl *sc;
1077 wxTextCtrl *tc;
1078 wxCheckBox *cb;
1079 wxChoice *ch;
1080
1081 for (int id = FEFirstID; id < FELastID; id++)
1082 {
1083 wxWindow *wnd = parent->FindWindowById(id,parent);
1084 if (wnd != NULL)
1085 {
1086 wxString readstr;
1087 long readlong;
1088 bool readbool;
1089 switch(id)
1090 {
1091 // Listbox
1092 case FEFormatID:
1093 case FECodecID:
1094 lb = dynamic_cast<wxListBox*>(wnd);
1095 readstr = preset->mControlState[id - FEFirstID];
1096 readlong = lb->FindString(readstr);
1097 if (readlong > -1) lb->Select(readlong);
1098 break;
1099 // Spin control
1100 case FEBitrateID:
1101 case FEQualityID:
1102 case FESampleRateID:
1103 case FECutoffID:
1104 case FEFrameSizeID:
1105 case FEBufSizeID:
1106 case FECompLevelID:
1107 case FELPCCoeffsID:
1108 case FEMinPredID:
1109 case FEMaxPredID:
1110 case FEMinPartOrderID:
1111 case FEMaxPartOrderID:
1112 case FEMuxRateID:
1113 case FEPacketSizeID:
1114 sc = dynamic_cast<wxSpinCtrl*>(wnd);
1115 preset->mControlState[id - FEFirstID].ToLong(&readlong);
1116 sc->SetValue(readlong);
1117 break;
1118 // Text control
1119 case FELanguageID:
1120 case FETagID:
1121 tc = dynamic_cast<wxTextCtrl*>(wnd);
1122 tc->SetValue(preset->mControlState[id - FEFirstID]);
1123 break;
1124 // Choice
1125 case FEProfileID:
1126 case FEPredOrderID:
1127 ch = dynamic_cast<wxChoice*>(wnd);
1128 preset->mControlState[id - FEFirstID].ToLong(&readlong);
1129 if (readlong > -1) ch->Select(readlong);
1130 break;
1131 // Check box
1132 case FEUseLPCID:
1133 case FEBitReservoirID:
1134 case FEVariableBlockLenID:
1135 cb = dynamic_cast<wxCheckBox*>(wnd);
1136 preset->mControlState[id - FEFirstID].ToLong(&readlong);
1137 if (readlong) readbool = true; else readbool = false;
1138 cb->SetValue(readbool);
1139 break;
1140 }
1141 }
1142 }
1143 }
1144
HandleXMLTag(const std::string_view & tag,const AttributesList & attrs)1145 bool FFmpegPresets::HandleXMLTag(const std::string_view& tag, const AttributesList &attrs)
1146 {
1147 if (mAbortImport)
1148 {
1149 return false;
1150 }
1151
1152 if (tag == "ffmpeg_presets")
1153 {
1154 return true;
1155 }
1156
1157 if (tag == "preset")
1158 {
1159 for (auto pair : attrs)
1160 {
1161 auto attr = pair.first;
1162 auto value = pair.second;
1163
1164 if (attr == "name")
1165 {
1166 wxString strValue = value.ToWString();
1167 mPreset = FindPreset(strValue);
1168
1169 if (mPreset)
1170 {
1171 auto query = XO("Replace preset '%s'?").Format( strValue );
1172 int action = AudacityMessageBox(
1173 query,
1174 XO("Confirm Overwrite"),
1175 wxYES_NO | wxCANCEL | wxCENTRE);
1176 if (action == wxCANCEL)
1177 {
1178 mAbortImport = true;
1179 return false;
1180 }
1181 if (action == wxNO)
1182 {
1183 mPreset = NULL;
1184 return false;
1185 }
1186 *mPreset = FFmpegPreset();
1187 }
1188 else
1189 {
1190 mPreset = &mPresets[strValue];
1191 }
1192
1193 mPreset->mPresetName = strValue;
1194 }
1195 }
1196 return true;
1197 }
1198
1199 if (tag == "setctrlstate" && mPreset)
1200 {
1201 long id = -1;
1202 for (auto pair : attrs)
1203 {
1204 auto attr = pair.first;
1205 auto value = pair.second;
1206
1207 if (attr == "id")
1208 {
1209 for (long i = FEFirstID; i < FELastID; i++)
1210 if (!wxStrcmp(FFmpegExportCtrlIDNames[i - FEFirstID], value.ToWString()))
1211 id = i;
1212 }
1213 else if (attr == "state")
1214 {
1215 if (id > FEFirstID && id < FELastID)
1216 mPreset->mControlState[id - FEFirstID] = value.ToWString();
1217 }
1218 }
1219 return true;
1220 }
1221
1222 return false;
1223 }
1224
HandleXMLChild(const std::string_view & tag)1225 XMLTagHandler *FFmpegPresets::HandleXMLChild(const std::string_view& tag)
1226 {
1227 if (mAbortImport)
1228 {
1229 return NULL;
1230 }
1231
1232 if (tag == "preset")
1233 {
1234 return this;
1235 }
1236 else if (tag == "setctrlstate")
1237 {
1238 return this;
1239 }
1240 return NULL;
1241 }
1242
WriteXMLHeader(XMLWriter & xmlFile) const1243 void FFmpegPresets::WriteXMLHeader(XMLWriter &xmlFile) const
1244 // may throw
1245 {
1246 xmlFile.Write(wxT("<?xml "));
1247 xmlFile.Write(wxT("version=\"1.0\" "));
1248 xmlFile.Write(wxT("standalone=\"no\" "));
1249 xmlFile.Write(wxT("?>\n"));
1250
1251 wxString dtdName = wxT("-//audacityffmpegpreset-1.0.0//DTD//EN");
1252 wxString dtdURI =
1253 wxT("http://audacity.sourceforge.net/xml/audacityffmpegpreset-1.0.0.dtd");
1254
1255 xmlFile.Write(wxT("<!DOCTYPE "));
1256 xmlFile.Write(wxT("project "));
1257 xmlFile.Write(wxT("PUBLIC "));
1258 xmlFile.Write(wxT("\"-//audacityffmpegpreset-1.0.0//DTD//EN\" "));
1259 xmlFile.Write(wxT("\"http://audacity.sourceforge.net/xml/audacityffmpegpreset-1.0.0.dtd\" "));
1260 xmlFile.Write(wxT(">\n"));
1261 }
1262
WriteXML(XMLWriter & xmlFile) const1263 void FFmpegPresets::WriteXML(XMLWriter &xmlFile) const
1264 // may throw
1265 {
1266 xmlFile.StartTag(wxT("ffmpeg_presets"));
1267 xmlFile.WriteAttr(wxT("version"),wxT("1.0"));
1268 FFmpegPresetMap::const_iterator iter;
1269 for (iter = mPresets.begin(); iter != mPresets.end(); ++iter)
1270 {
1271 auto preset = &iter->second;
1272 xmlFile.StartTag(wxT("preset"));
1273 xmlFile.WriteAttr(wxT("name"),preset->mPresetName);
1274 for (long i = FEFirstID + 1; i < FELastID; i++)
1275 {
1276 xmlFile.StartTag(wxT("setctrlstate"));
1277 xmlFile.WriteAttr(wxT("id"),wxString(FFmpegExportCtrlIDNames[i - FEFirstID]));
1278 xmlFile.WriteAttr(wxT("state"),preset->mControlState[i - FEFirstID]);
1279 xmlFile.EndTag(wxT("setctrlstate"));
1280 }
1281 xmlFile.EndTag(wxT("preset"));
1282 }
1283 xmlFile.EndTag(wxT("ffmpeg_presets"));
1284 }
1285
1286 //----------------------------------------------------------------------------
1287 // ExportFFmpegOptions Class
1288 //----------------------------------------------------------------------------
1289
1290 BEGIN_EVENT_TABLE(ExportFFmpegOptions, wxDialogWrapper)
1291 EVT_BUTTON(wxID_OK,ExportFFmpegOptions::OnOK)
1292 EVT_BUTTON(wxID_HELP,ExportFFmpegOptions::OnGetURL)
1293 EVT_LISTBOX(FEFormatID,ExportFFmpegOptions::OnFormatList)
1294 EVT_LISTBOX(FECodecID,ExportFFmpegOptions::OnCodecList)
1295 EVT_BUTTON(FEAllFormatsID,ExportFFmpegOptions::OnAllFormats)
1296 EVT_BUTTON(FEAllCodecsID,ExportFFmpegOptions::OnAllCodecs)
1297 EVT_BUTTON(FESavePresetID,ExportFFmpegOptions::OnSavePreset)
1298 EVT_BUTTON(FELoadPresetID,ExportFFmpegOptions::OnLoadPreset)
1299 EVT_BUTTON(FEDeletePresetID,ExportFFmpegOptions::OnDeletePreset)
1300 EVT_BUTTON(FEImportPresetsID,ExportFFmpegOptions::OnImportPresets)
1301 EVT_BUTTON(FEExportPresetsID,ExportFFmpegOptions::OnExportPresets)
1302 END_EVENT_TABLE()
1303
1304 /// Format-codec compatibility list
1305 /// Must end with NULL entry
1306 CompatibilityEntry ExportFFmpegOptions::CompatibilityList[] =
1307 {
1308 { wxT("adts"), AUDACITY_AV_CODEC_ID_AAC },
1309
1310 { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1311 { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S8 },
1312 { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S24BE },
1313 { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S32BE },
1314 { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1315 { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1316 { wxT("aiff"), AUDACITY_AV_CODEC_ID_MACE3 },
1317 { wxT("aiff"), AUDACITY_AV_CODEC_ID_MACE6 },
1318 { wxT("aiff"), AUDACITY_AV_CODEC_ID_GSM },
1319 { wxT("aiff"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1320 { wxT("aiff"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1321 { wxT("aiff"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_QT },
1322 { wxT("aiff"), AUDACITY_AV_CODEC_ID_QDM2 },
1323
1324 { wxT("amr"), AUDACITY_AV_CODEC_ID_AMR_NB },
1325 { wxT("amr"), AUDACITY_AV_CODEC_ID_AMR_WB },
1326
1327 { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1328 { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1329 { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1330 { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1331 { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1332 { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1333 { wxT("asf"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1334 { wxT("asf"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1335 { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1336 { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1337 { wxT("asf"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1338 { wxT("asf"), AUDACITY_AV_CODEC_ID_GSM_MS },
1339 { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1340 //{ wxT("asf"), AUDACITY_AV_CODEC_ID_MP2 }, Bug 59
1341 { wxT("asf"), AUDACITY_AV_CODEC_ID_MP3 },
1342 #if LIBAVCODEC_VERSION_MAJOR < 58
1343 { wxT("asf"), AUDACITY_AV_CODEC_ID_VOXWARE },
1344 #endif
1345 { wxT("asf"), AUDACITY_AV_CODEC_ID_AAC },
1346 { wxT("asf"), AUDACITY_AV_CODEC_ID_WMAV1 },
1347 { wxT("asf"), AUDACITY_AV_CODEC_ID_WMAV2 },
1348 { wxT("asf"), AUDACITY_AV_CODEC_ID_WMAPRO },
1349 { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1350 { wxT("asf"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1351 { wxT("asf"), AUDACITY_AV_CODEC_ID_IMC },
1352 { wxT("asf"), AUDACITY_AV_CODEC_ID_AC3 },
1353 { wxT("asf"), AUDACITY_AV_CODEC_ID_DTS },
1354 { wxT("asf"), AUDACITY_AV_CODEC_ID_FLAC },
1355 { wxT("asf"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1356 { wxT("asf"), AUDACITY_AV_CODEC_ID_VORBIS },
1357
1358 { wxT("au"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1359 { wxT("au"), AUDACITY_AV_CODEC_ID_PCM_S8 },
1360 { wxT("au"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1361 { wxT("au"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1362
1363 { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1364 { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1365 { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1366 { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1367 { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1368 { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1369 { wxT("avi"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1370 { wxT("avi"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1371 { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1372 { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1373 { wxT("avi"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1374 { wxT("avi"), AUDACITY_AV_CODEC_ID_GSM_MS },
1375 { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1376 // { wxT("avi"), AUDACITY_AV_CODEC_ID_MP2 }, //Bug 59
1377 { wxT("avi"), AUDACITY_AV_CODEC_ID_MP3 },
1378 #if LIBAVCODEC_VERSION_MAJOR < 58
1379 { wxT("avi"), AUDACITY_AV_CODEC_ID_VOXWARE },
1380 #endif
1381 { wxT("avi"), AUDACITY_AV_CODEC_ID_AAC },
1382 { wxT("avi"), AUDACITY_AV_CODEC_ID_WMAV1 },
1383 { wxT("avi"), AUDACITY_AV_CODEC_ID_WMAV2 },
1384 { wxT("avi"), AUDACITY_AV_CODEC_ID_WMAPRO },
1385 { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1386 { wxT("avi"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1387 { wxT("avi"), AUDACITY_AV_CODEC_ID_IMC },
1388 { wxT("avi"), AUDACITY_AV_CODEC_ID_AC3 },
1389 { wxT("avi"), AUDACITY_AV_CODEC_ID_DTS },
1390 { wxT("avi"), AUDACITY_AV_CODEC_ID_FLAC },
1391 { wxT("avi"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1392 { wxT("avi"), AUDACITY_AV_CODEC_ID_VORBIS },
1393
1394 { wxT("crc"), AUDACITY_AV_CODEC_ID_NONE },
1395
1396 { wxT("dv"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1397
1398 { wxT("ffm"), AUDACITY_AV_CODEC_ID_NONE },
1399
1400 { wxT("flv"), AUDACITY_AV_CODEC_ID_MP3 },
1401 { wxT("flv"), AUDACITY_AV_CODEC_ID_PCM_S8 },
1402 { wxT("flv"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1403 { wxT("flv"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1404 { wxT("flv"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1405 { wxT("flv"), AUDACITY_AV_CODEC_ID_AAC },
1406 { wxT("flv"), AUDACITY_AV_CODEC_ID_NELLYMOSER },
1407
1408 { wxT("framecrc"), AUDACITY_AV_CODEC_ID_NONE },
1409
1410 { wxT("gxf"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1411
1412 { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1413 { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1414 { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1415 { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1416 { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1417 { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1418 { wxT("matroska"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1419 { wxT("matroska"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1420 { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1421 { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1422 { wxT("matroska"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1423 { wxT("matroska"), AUDACITY_AV_CODEC_ID_GSM_MS },
1424 { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1425 // { wxT("matroska"), AUDACITY_AV_CODEC_ID_MP2 }, // Bug 59
1426 { wxT("matroska"), AUDACITY_AV_CODEC_ID_MP3 },
1427 #if LIBAVCODEC_VERSION_MAJOR < 58
1428 { wxT("matroska"), AUDACITY_AV_CODEC_ID_VOXWARE },
1429 #endif
1430 { wxT("matroska"), AUDACITY_AV_CODEC_ID_AAC },
1431 { wxT("matroska"), AUDACITY_AV_CODEC_ID_WMAV1 },
1432 { wxT("matroska"), AUDACITY_AV_CODEC_ID_WMAV2 },
1433 { wxT("matroska"), AUDACITY_AV_CODEC_ID_WMAPRO },
1434 { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1435 { wxT("matroska"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1436 { wxT("matroska"), AUDACITY_AV_CODEC_ID_IMC },
1437 { wxT("matroska"), AUDACITY_AV_CODEC_ID_AC3 },
1438 { wxT("matroska"), AUDACITY_AV_CODEC_ID_DTS },
1439 { wxT("matroska"), AUDACITY_AV_CODEC_ID_FLAC },
1440 { wxT("matroska"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1441 { wxT("matroska"), AUDACITY_AV_CODEC_ID_VORBIS },
1442
1443 { wxT("mmf"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1444
1445 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S32BE }, //mov
1446 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1447 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S24BE },
1448 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1449 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1450 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1451 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_S8 },
1452 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1453 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1454 { wxT("mov"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1455 { wxT("mov"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_QT },
1456 { wxT("mov"), AUDACITY_AV_CODEC_ID_MACE3 },
1457 { wxT("mov"), AUDACITY_AV_CODEC_ID_MACE6 },
1458 { wxT("mov"), AUDACITY_AV_CODEC_ID_MP3 },
1459 { wxT("mov"), AUDACITY_AV_CODEC_ID_AAC },
1460 { wxT("mov"), AUDACITY_AV_CODEC_ID_AMR_NB },
1461 { wxT("mov"), AUDACITY_AV_CODEC_ID_AMR_WB },
1462 { wxT("mov"), AUDACITY_AV_CODEC_ID_GSM },
1463 { wxT("mov"), AUDACITY_AV_CODEC_ID_ALAC },
1464 { wxT("mov"), AUDACITY_AV_CODEC_ID_QCELP },
1465 { wxT("mov"), AUDACITY_AV_CODEC_ID_QDM2 },
1466 { wxT("mov"), AUDACITY_AV_CODEC_ID_DVAUDIO },
1467 { wxT("mov"), AUDACITY_AV_CODEC_ID_WMAV2 },
1468 { wxT("mov"), AUDACITY_AV_CODEC_ID_ALAC },
1469
1470 { wxT("mp4"), AUDACITY_AV_CODEC_ID_AAC },
1471 { wxT("mp4"), AUDACITY_AV_CODEC_ID_QCELP },
1472 { wxT("mp4"), AUDACITY_AV_CODEC_ID_MP3 },
1473 { wxT("mp4"), AUDACITY_AV_CODEC_ID_VORBIS },
1474
1475 { wxT("psp"), AUDACITY_AV_CODEC_ID_AAC },
1476 { wxT("psp"), AUDACITY_AV_CODEC_ID_QCELP },
1477 { wxT("psp"), AUDACITY_AV_CODEC_ID_MP3 },
1478 { wxT("psp"), AUDACITY_AV_CODEC_ID_VORBIS },
1479
1480 { wxT("ipod"), AUDACITY_AV_CODEC_ID_AAC },
1481 { wxT("ipod"), AUDACITY_AV_CODEC_ID_QCELP },
1482 { wxT("ipod"), AUDACITY_AV_CODEC_ID_MP3 },
1483 { wxT("ipod"), AUDACITY_AV_CODEC_ID_VORBIS },
1484
1485 { wxT("3gp"), AUDACITY_AV_CODEC_ID_AAC },
1486 { wxT("3gp"), AUDACITY_AV_CODEC_ID_AMR_NB },
1487 { wxT("3gp"), AUDACITY_AV_CODEC_ID_AMR_WB },
1488
1489 { wxT("3g2"), AUDACITY_AV_CODEC_ID_AAC },
1490 { wxT("3g2"), AUDACITY_AV_CODEC_ID_AMR_NB },
1491 { wxT("3g2"), AUDACITY_AV_CODEC_ID_AMR_WB },
1492
1493 { wxT("mp3"), AUDACITY_AV_CODEC_ID_MP3 },
1494
1495 { wxT("mpeg"), AUDACITY_AV_CODEC_ID_AC3 },
1496 { wxT("mpeg"), AUDACITY_AV_CODEC_ID_DTS },
1497 { wxT("mpeg"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1498 //{ wxT("mpeg"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1499
1500 { wxT("vcd"), AUDACITY_AV_CODEC_ID_AC3 },
1501 { wxT("vcd"), AUDACITY_AV_CODEC_ID_DTS },
1502 { wxT("vcd"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1503 //{ wxT("vcd"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1504
1505 { wxT("vob"), AUDACITY_AV_CODEC_ID_AC3 },
1506 { wxT("vob"), AUDACITY_AV_CODEC_ID_DTS },
1507 { wxT("vob"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1508 //{ wxT("vob"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1509
1510 { wxT("svcd"), AUDACITY_AV_CODEC_ID_AC3 },
1511 { wxT("svcd"), AUDACITY_AV_CODEC_ID_DTS },
1512 { wxT("svcd"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1513 //{ wxT("svcd"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1514
1515 { wxT("dvd"), AUDACITY_AV_CODEC_ID_AC3 },
1516 { wxT("dvd"), AUDACITY_AV_CODEC_ID_DTS },
1517 { wxT("dvd"), AUDACITY_AV_CODEC_ID_PCM_S16BE },
1518 //{ wxT("dvd"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1519
1520 { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1521 { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1522 { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1523 { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1524 { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1525 { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1526 { wxT("nut"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1527 { wxT("nut"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1528 { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1529 { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1530 { wxT("nut"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1531 { wxT("nut"), AUDACITY_AV_CODEC_ID_GSM_MS },
1532 { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1533 //{ wxT("nut"), AUDACITY_AV_CODEC_ID_MP2 },// Bug 59
1534 { wxT("nut"), AUDACITY_AV_CODEC_ID_MP3 },
1535 #if LIBAVCODEC_VERSION_MAJOR < 58
1536 { wxT("nut"), AUDACITY_AV_CODEC_ID_VOXWARE },
1537 #endif
1538 { wxT("nut"), AUDACITY_AV_CODEC_ID_AAC },
1539 { wxT("nut"), AUDACITY_AV_CODEC_ID_WMAV1 },
1540 { wxT("nut"), AUDACITY_AV_CODEC_ID_WMAV2 },
1541 { wxT("nut"), AUDACITY_AV_CODEC_ID_WMAPRO },
1542 { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1543 { wxT("nut"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1544 { wxT("nut"), AUDACITY_AV_CODEC_ID_IMC },
1545 { wxT("nut"), AUDACITY_AV_CODEC_ID_AC3 },
1546 { wxT("nut"), AUDACITY_AV_CODEC_ID_DTS },
1547 { wxT("nut"), AUDACITY_AV_CODEC_ID_FLAC },
1548 { wxT("nut"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1549 { wxT("nut"), AUDACITY_AV_CODEC_ID_VORBIS },
1550
1551 { wxT("ogg"), AUDACITY_AV_CODEC_ID_VORBIS },
1552 { wxT("ogg"), AUDACITY_AV_CODEC_ID_FLAC },
1553
1554 { wxT("ac3"), AUDACITY_AV_CODEC_ID_AC3 },
1555
1556 { wxT("dts"), AUDACITY_AV_CODEC_ID_DTS },
1557
1558 { wxT("flac"), AUDACITY_AV_CODEC_ID_FLAC },
1559
1560 { wxT("RoQ"), AUDACITY_AV_CODEC_ID_ROQ_DPCM },
1561
1562 { wxT("rm"), AUDACITY_AV_CODEC_ID_AC3 },
1563
1564 { wxT("swf"), AUDACITY_AV_CODEC_ID_MP3 },
1565
1566 { wxT("avm2"), AUDACITY_AV_CODEC_ID_MP3 },
1567
1568 { wxT("voc"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1569
1570 { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_S16LE },
1571 { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_U8 },
1572 { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_S24LE },
1573 { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_S32LE },
1574 { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_MS },
1575 { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_ALAW },
1576 { wxT("wav"), AUDACITY_AV_CODEC_ID_PCM_MULAW },
1577 { wxT("wav"), AUDACITY_AV_CODEC_ID_WMAVOICE },
1578 { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_IMA_WAV },
1579 { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_YAMAHA },
1580 { wxT("wav"), AUDACITY_AV_CODEC_ID_TRUESPEECH },
1581 { wxT("wav"), AUDACITY_AV_CODEC_ID_GSM_MS },
1582 { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_G726 },
1583 //{ wxT("wav"), AUDACITY_AV_CODEC_ID_MP2 }, Bug 59 - It crashes.
1584 { wxT("wav"), AUDACITY_AV_CODEC_ID_MP3 },
1585 #if LIBAVCODEC_VERSION_MAJOR < 58
1586 { wxT("wav"), AUDACITY_AV_CODEC_ID_VOXWARE },
1587 #endif
1588 { wxT("wav"), AUDACITY_AV_CODEC_ID_AAC },
1589 // { wxT("wav"), AUDACITY_AV_CODEC_ID_WMAV1 },
1590 // { wxT("wav"), AUDACITY_AV_CODEC_ID_WMAV2 },
1591 { wxT("wav"), AUDACITY_AV_CODEC_ID_WMAPRO },
1592 { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_CT },
1593 { wxT("wav"), AUDACITY_AV_CODEC_ID_ATRAC3 },
1594 { wxT("wav"), AUDACITY_AV_CODEC_ID_IMC },
1595 { wxT("wav"), AUDACITY_AV_CODEC_ID_AC3 },
1596 //{ wxT("wav"), AUDACITY_AV_CODEC_ID_DTS },
1597 { wxT("wav"), AUDACITY_AV_CODEC_ID_FLAC },
1598 { wxT("wav"), AUDACITY_AV_CODEC_ID_ADPCM_SWF },
1599 { wxT("wav"), AUDACITY_AV_CODEC_ID_VORBIS },
1600
1601 { NULL, AUDACITY_AV_CODEC_ID_NONE }
1602 };
1603
1604 /// AAC profiles
1605 // The FF_PROFILE_* enumeration is defined in the ffmpeg library
1606 // PRL: I cant find where this preference is used!
1607 ChoiceSetting AACProfiles { wxT("/FileFormats/FFmpegAACProfile"),
1608 {
1609 {wxT("1") /*FF_PROFILE_AAC_LOW*/, XO("LC")},
1610 {wxT("0") /*FF_PROFILE_AAC_MAIN*/, XO("Main")},
1611 // {wxT("2") /*FF_PROFILE_AAC_SSR*/, XO("SSR")}, //SSR is not supported
1612 {wxT("3") /*FF_PROFILE_AAC_LTP*/, XO("LTP")},
1613 },
1614 0, // "1"
1615 };
1616
1617 /// List of export types
1618 ExposedFormat ExportFFmpegOptions::fmts[] =
1619 {
1620 {FMT_M4A, wxT("M4A"), wxT("m4a"), wxT("ipod"), 48, AV_CANMETA, true, XO("M4A (AAC) Files (FFmpeg)"), AUDACITY_AV_CODEC_ID_AAC, true},
1621 {FMT_AC3, wxT("AC3"), wxT("ac3"), wxT("ac3"), 7, AV_VERSION_INT(0,0,0), false, XO("AC3 Files (FFmpeg)"), AUDACITY_AV_CODEC_ID_AC3, true},
1622 {FMT_AMRNB, wxT("AMRNB"), wxT("amr"), wxT("amr"), 1, AV_VERSION_INT(0,0,0), false, XO("AMR (narrow band) Files (FFmpeg)"), AUDACITY_AV_CODEC_ID_AMR_NB, true},
1623 {FMT_OPUS, wxT("OPUS"), wxT("opus"), wxT("opus"), 255, AV_CANMETA, true, XO("Opus (OggOpus) Files (FFmpeg)"), AUDACITY_AV_CODEC_ID_OPUS, true},
1624 {FMT_WMA2, wxT("WMA"), wxT("wma"), wxT("asf"), 2, AV_VERSION_INT(52,53,0), false, XO("WMA (version 2) Files (FFmpeg)"), AUDACITY_AV_CODEC_ID_WMAV2, true},
1625 {FMT_OTHER, wxT("FFMPEG"), wxT(""), wxT(""), 255, AV_CANMETA, true, XO("Custom FFmpeg Export"), AUDACITY_AV_CODEC_ID_NONE, true}
1626 };
1627
1628 /// Some controls (parameters they represent) are only applicable to a number
1629 /// of codecs and/or formats.
1630 /// Syntax: first, enable a control for each applicable format-codec combination
1631 /// then disable it for anything else
1632 /// "any" - any format
1633 /// AUDACITY_AV_CODEC_ID_NONE - any codec
1634 /// This list must end with {FALSE,FFmpegExportCtrlID(0),AUDACITY_AV_CODEC_ID_NONE,NULL}
1635 ApplicableFor ExportFFmpegOptions::apptable[] =
1636 {
1637 {TRUE,FEQualityID,AUDACITY_AV_CODEC_ID_AAC,"any"},
1638 {TRUE,FEQualityID,AUDACITY_AV_CODEC_ID_MP3,"any"},
1639 {TRUE,FEQualityID,AUDACITY_AV_CODEC_ID_VORBIS,"any"},
1640 {FALSE,FEQualityID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1641
1642 {TRUE,FECutoffID,AUDACITY_AV_CODEC_ID_AC3,"any"},
1643 {TRUE,FECutoffID,AUDACITY_AV_CODEC_ID_AAC,"any"},
1644 {TRUE,FECutoffID,AUDACITY_AV_CODEC_ID_VORBIS,"any"},
1645 {FALSE,FECutoffID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1646
1647 {TRUE,FEFrameSizeID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1648 {FALSE,FEFrameSizeID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1649
1650 {TRUE,FEProfileID,AUDACITY_AV_CODEC_ID_AAC,"any"},
1651 {FALSE,FEProfileID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1652
1653 {TRUE,FECompLevelID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1654 {FALSE,FECompLevelID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1655
1656 {TRUE,FEUseLPCID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1657 {FALSE,FEUseLPCID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1658
1659 {TRUE,FELPCCoeffsID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1660 {FALSE,FELPCCoeffsID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1661
1662 {TRUE,FEMinPredID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1663 {FALSE,FEMinPredID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1664
1665 {TRUE,FEMaxPredID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1666 {FALSE,FEMaxPredID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1667
1668 {TRUE,FEPredOrderID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1669 {FALSE,FEPredOrderID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1670
1671 {TRUE,FEMinPartOrderID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1672 {FALSE,FEMinPartOrderID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1673
1674 {TRUE,FEMaxPartOrderID,AUDACITY_AV_CODEC_ID_FLAC,"any"},
1675 {FALSE,FEMaxPartOrderID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1676
1677 {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"mpeg"},
1678 {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"vcd"},
1679 {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"vob"},
1680 {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"svcd"},
1681 {TRUE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"dvd"},
1682 {FALSE,FEMuxRateID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1683
1684 {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"mpeg"},
1685 {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"vcd"},
1686 {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"vob"},
1687 {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"svcd"},
1688 {TRUE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"dvd"},
1689 {FALSE,FEPacketSizeID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1690
1691 {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"matroska"},
1692 {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"mov"},
1693 {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"3gp"},
1694 {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"mp4"},
1695 {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"psp"},
1696 {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"3g2"},
1697 {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"ipod"},
1698 {TRUE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"mpegts"},
1699 {FALSE,FELanguageID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1700
1701 {TRUE,FEBitReservoirID,AUDACITY_AV_CODEC_ID_MP3,"any"},
1702 {TRUE,FEBitReservoirID,AUDACITY_AV_CODEC_ID_WMAV1,"any"},
1703 {TRUE,FEBitReservoirID,AUDACITY_AV_CODEC_ID_WMAV2,"any"},
1704 {FALSE,FEBitReservoirID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1705
1706 {TRUE,FEVariableBlockLenID,AUDACITY_AV_CODEC_ID_WMAV1,"any"},
1707 {TRUE,FEVariableBlockLenID,AUDACITY_AV_CODEC_ID_WMAV2,"any"},
1708 {FALSE,FEVariableBlockLenID,AUDACITY_AV_CODEC_ID_NONE,"any"},
1709
1710 {FALSE,FFmpegExportCtrlID(0),AUDACITY_AV_CODEC_ID_NONE,NULL}
1711 };
1712
1713 namespace {
1714
1715 /// Prediction order method - names.
1716 const TranslatableStrings PredictionOrderMethodNames {
1717 XO("Estimate"),
1718 XO("2-level"),
1719 XO("4-level"),
1720 XO("8-level"),
1721 XO("Full search"),
1722 XO("Log search"),
1723 };
1724
1725 }
1726
1727
1728
~ExportFFmpegOptions()1729 ExportFFmpegOptions::~ExportFFmpegOptions()
1730 {
1731 }
1732
ExportFFmpegOptions(wxWindow * parent)1733 ExportFFmpegOptions::ExportFFmpegOptions(wxWindow *parent)
1734 : wxDialogWrapper(parent, wxID_ANY,
1735 XO("Configure custom FFmpeg options"))
1736 {
1737 SetName();
1738 ShuttleGui S(this, eIsCreatingFromPrefs);
1739 mFFmpeg = FFmpegFunctions::Load();
1740 //FFmpegLibsInst()->LoadLibs(NULL,true); //Loaded at startup or from Prefs now
1741
1742 mPresets = std::make_unique<FFmpegPresets>();
1743 mPresets->GetPresetList(mPresetNames);
1744
1745 if (mFFmpeg)
1746 {
1747 FetchFormatList();
1748 FetchCodecList();
1749
1750 PopulateOrExchange(S);
1751
1752 //Select the format that was selected last time this dialog was closed
1753 mFormatList->Select(mFormatList->FindString(gPrefs->Read(wxT("/FileFormats/FFmpegFormat"))));
1754 DoOnFormatList();
1755
1756 //Select the codec that was selected last time this dialog was closed
1757 auto codec = mFFmpeg->CreateEncoder(gPrefs->Read(wxT("/FileFormats/FFmpegCodec")).ToUTF8());
1758
1759 if (codec != nullptr)
1760 mCodecList->Select(mCodecList->FindString(wxString::FromUTF8(codec->GetName())));
1761
1762 DoOnCodecList();
1763 }
1764
1765 }
1766
1767 ///
1768 ///
FetchFormatList()1769 void ExportFFmpegOptions::FetchFormatList()
1770 {
1771 if (!mFFmpeg)
1772 return;
1773
1774 // Enumerate all output formats
1775 std::unique_ptr<AVOutputFormatWrapper> ofmt;
1776
1777 while ((ofmt = mFFmpeg->GetNextOutputFormat(ofmt.get()))!=NULL)
1778 {
1779 // Any audio-capable format has default audio codec.
1780 // If it doesn't, then it doesn't supports any audio codecs
1781 if (ofmt->GetAudioCodec() != AUDACITY_AV_CODEC_ID_NONE)
1782 {
1783 mFormatNames.push_back(wxString::FromUTF8(ofmt->GetName()));
1784 mFormatLongNames.push_back(wxString::Format(wxT("%s - %s"),mFormatNames.back(),wxString::FromUTF8(ofmt->GetLongName())));
1785 }
1786 }
1787 // Show all formats
1788 mShownFormatNames = mFormatNames;
1789 mShownFormatLongNames = mFormatLongNames;
1790 }
1791
1792 ///
1793 ///
FetchCodecList()1794 void ExportFFmpegOptions::FetchCodecList()
1795 {
1796 if (!mFFmpeg)
1797 return;
1798 // Enumerate all codecs
1799 std::unique_ptr<AVCodecWrapper> codec;
1800 while ((codec = mFFmpeg->GetNextCodec(codec.get()))!=NULL)
1801 {
1802 // We're only interested in audio and only in encoders
1803 if (codec->IsAudio() && mFFmpeg->av_codec_is_encoder(codec->GetWrappedValue()))
1804 {
1805 // MP2 Codec is broken. Don't allow it.
1806 if( codec->GetId() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_MP2))
1807 continue;
1808
1809 mCodecNames.push_back(wxString::FromUTF8(codec->GetName()));
1810 mCodecLongNames.push_back(wxString::Format(wxT("%s - %s"),mCodecNames.back(),wxString::FromUTF8(codec->GetLongName())));
1811 }
1812 }
1813 // Show all codecs
1814 mShownCodecNames = mCodecNames;
1815 mShownCodecLongNames = mCodecLongNames;
1816 }
1817
1818 ///
1819 ///
PopulateOrExchange(ShuttleGui & S)1820 void ExportFFmpegOptions::PopulateOrExchange(ShuttleGui & S)
1821 {
1822 S.StartVerticalLay(1);
1823 S.StartMultiColumn(1, wxEXPAND);
1824 {
1825 S.SetStretchyRow(3);
1826 S.StartMultiColumn(7, wxEXPAND);
1827 {
1828 S.SetStretchyCol(1);
1829 mPresetCombo = S.Id(FEPresetID).AddCombo(XXO("Preset:"), gPrefs->Read(wxT("/FileFormats/FFmpegPreset"),wxEmptyString), mPresetNames);
1830 S.Id(FELoadPresetID).AddButton(XXO("Load Preset"));
1831 S.Id(FESavePresetID).AddButton(XXO("Save Preset"));
1832 S.Id(FEDeletePresetID).AddButton(XXO("Delete Preset"));
1833 S.Id(FEImportPresetsID).AddButton(XXO("Import Presets"));
1834 S.Id(FEExportPresetsID).AddButton(XXO("Export Presets"));
1835 }
1836 S.EndMultiColumn();
1837 S.StartMultiColumn(4, wxALIGN_LEFT);
1838 {
1839 S.SetStretchyCol(1);
1840 S.SetStretchyCol(3);
1841 S.Id(FEFormatLabelID).AddFixedText(XO("Format:"));
1842 mFormatName = S.Id(FEFormatNameID).AddVariableText( {} );
1843 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
1844 S.Id(FECodecLabelID).AddFixedText(XO("Codec:"));
1845 mCodecName = S.Id(FECodecNameID).AddVariableText( {} );
1846 }
1847 S.EndMultiColumn();
1848 S.AddVariableText(XO(
1849 "Not all formats and codecs are compatible. Nor are all option combinations compatible with all codecs."),
1850 false);
1851 S.StartMultiColumn(2, wxEXPAND);
1852 {
1853 S.StartMultiColumn(2, wxEXPAND);
1854 {
1855 S.SetStretchyRow(1);
1856 S.Id(FEAllFormatsID).AddButton(XXO("Show All Formats"));
1857 S.Id(FEAllCodecsID).AddButton(XXO("Show All Codecs"));
1858 mFormatList = S.Id(FEFormatID).AddListBox(mFormatNames);
1859 mFormatList->DeselectAll();
1860 mCodecList = S.Id(FECodecID).AddListBox(mCodecNames);
1861 mCodecList->DeselectAll();
1862 }
1863 S.EndMultiColumn();
1864 S.StartVerticalLay();
1865 {
1866 //S.StartScroller( );
1867 S.SetBorder( 3 );
1868 S.StartStatic(XO("General Options"), 0);
1869 {
1870 S.StartMultiColumn(8, wxEXPAND);
1871 {
1872 S.Id(FELanguageID)
1873 .ToolTip(XO("ISO 639 3-letter language code\nOptional\nempty - automatic"))
1874 .TieTextBox(XXO("Language:"), {wxT("/FileFormats/FFmpegLanguage"), wxEmptyString}, 9);
1875
1876 S.AddSpace( 20,0 );
1877 S.AddVariableText(XO("Bit Reservoir"));
1878 S.Id(FEBitReservoirID).TieCheckBox( {}, {wxT("/FileFormats/FFmpegBitReservoir"), true});
1879
1880 S.AddSpace( 20,0 );
1881 S.AddVariableText(XO("VBL"));
1882 S.Id(FEVariableBlockLenID).TieCheckBox( {}, {wxT("/FileFormats/FFmpegVariableBlockLen"), true});
1883 }
1884 S.EndMultiColumn();
1885 S.StartMultiColumn(4, wxALIGN_LEFT);
1886 {
1887 S.Id(FETagID)
1888 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
1889 .ToolTip(XO("Codec tag (FOURCC)\nOptional\nempty - automatic"))
1890 .TieTextBox(XXO("Tag:"), {wxT("/FileFormats/FFmpegTag"), wxEmptyString}, 4);
1891
1892 S.Id(FEBitrateID)
1893 .ToolTip(XO("Bit Rate (bits/second) - influences the resulting file size and quality\nSome codecs may only accept specific values (128k, 192k, 256k etc)\n0 - automatic\nRecommended - 192000"))
1894 .TieSpinCtrl(XXO("Bit Rate:"), {wxT("/FileFormats/FFmpegBitRate"), 0}, 1000000, 0);
1895
1896 S.Id(FEQualityID)
1897 .ToolTip(XO("Overall quality, used differently by different codecs\nRequired for vorbis\n0 - automatic\n-1 - off (use bitrate instead)"))
1898 .TieSpinCtrl(XXO("Quality:"), {wxT("/FileFormats/FFmpegQuality"), 0}, 500, -1);
1899
1900 S.Id(FESampleRateID)
1901 .ToolTip(XO("Sample rate (Hz)\n0 - don't change sample rate"))
1902 .TieSpinCtrl(XXO("Sample Rate:"), {wxT("/FileFormats/FFmpegSampleRate"), 0}, 200000, 0);
1903
1904 S.Id(FECutoffID)
1905 .ToolTip(XO("Audio cutoff bandwidth (Hz)\nOptional\n0 - automatic"))
1906 .TieSpinCtrl(XXO("Cutoff:"), {wxT("/FileFormats/FFmpegCutOff"), 0}, 10000000, 0);
1907
1908 // PRL: As commented elsewhere, this preference does nothing
1909 S.Id(FEProfileID)
1910 .ToolTip(XO("AAC Profile\nLow Complexity - default\nMost players won't play anything other than LC"))
1911 .MinSize( { 100, -1 } )
1912 .TieChoice(XXO("Profile:"), AACProfiles);
1913 }
1914 S.EndMultiColumn();
1915 }
1916 S.EndStatic();
1917 S.StartStatic(XO("FLAC options"),0);
1918 {
1919 S.StartMultiColumn(4, wxALIGN_LEFT);
1920 {
1921 S
1922 .ToolTip(XO("Compression level\nRequired for FLAC\n-1 - automatic\nmin - 0 (fast encoding, large output file)\nmax - 10 (slow encoding, small output file)"))
1923 .Id(FECompLevelID).TieSpinCtrl(XXO("Compression:"), {wxT("/FileFormats/FFmpegCompLevel"), 0}, 10, -1);
1924
1925 S.Id(FEFrameSizeID)
1926 .ToolTip(XO("Frame size\nOptional\n0 - default\nmin - 16\nmax - 65535"))
1927 .TieSpinCtrl(XXO("Frame:"), {wxT("/FileFormats/FFmpegFrameSize"), 0}, 65535, 0);
1928
1929 S.Id(FELPCCoeffsID)
1930 .ToolTip(XO("LPC coefficients precision\nOptional\n0 - default\nmin - 1\nmax - 15"))
1931 .TieSpinCtrl(XXO("LPC"), {wxT("/FileFormats/FFmpegLPCCoefPrec"), 0}, 15, 0);
1932
1933 S.Id(FEPredOrderID)
1934 .ToolTip(XO("Prediction Order Method\nEstimate - fastest, lower compression\nLog search - slowest, best compression\nFull search - default"))
1935 .MinSize( { 100, -1 } )
1936 .TieNumberAsChoice(
1937 XXO("PdO Method:"),
1938 {wxT("/FileFormats/FFmpegPredOrderMethod"),
1939 4}, // Full search
1940 PredictionOrderMethodNames
1941 );
1942
1943 S.Id(FEMinPredID)
1944 .ToolTip(XO("Minimal prediction order\nOptional\n-1 - default\nmin - 0\nmax - 32 (with LPC) or 4 (without LPC)"))
1945 .TieSpinCtrl(XXO("Min. PdO"), {wxT("/FileFormats/FFmpegMinPredOrder"), -1}, 32, -1);
1946
1947 S.Id(FEMaxPredID)
1948 .ToolTip(XO("Maximal prediction order\nOptional\n-1 - default\nmin - 0\nmax - 32 (with LPC) or 4 (without LPC)"))
1949 .TieSpinCtrl(XXO("Max. PdO"), {wxT("/FileFormats/FFmpegMaxPredOrder"), -1}, 32, -1);
1950
1951 S.Id(FEMinPartOrderID)
1952 .ToolTip(XO("Minimal partition order\nOptional\n-1 - default\nmin - 0\nmax - 8"))
1953 .TieSpinCtrl(XXO("Min. PtO"), {wxT("/FileFormats/FFmpegMinPartOrder"), -1}, 8, -1);
1954
1955 S.Id(FEMaxPartOrderID)
1956 .ToolTip(XO("Maximal partition order\nOptional\n-1 - default\nmin - 0\nmax - 8"))
1957 .TieSpinCtrl(XXO("Max. PtO"), {wxT("/FileFormats/FFmpegMaxPartOrder"), -1}, 8, -1);
1958
1959 /* i18n-hint: Abbreviates "Linear Predictive Coding",
1960 but this text needs to be kept very short */
1961 S.AddVariableText(XO("Use LPC"));
1962 // PRL: This preference is not used anywhere!
1963 S.Id(FEUseLPCID).TieCheckBox( {}, {wxT("/FileFormats/FFmpegUseLPC"), true});
1964 }
1965 S.EndMultiColumn();
1966 }
1967 S.EndStatic();
1968 S.StartStatic(XO("MPEG container options"),0);
1969 {
1970 S.StartMultiColumn(4, wxALIGN_LEFT);
1971 {
1972 S.Id(FEMuxRateID)
1973 .ToolTip(XO("Maximum bit rate of the multiplexed stream\nOptional\n0 - default"))
1974 /* i18n-hint: 'mux' is short for multiplexor, a device that selects between several inputs
1975 'Mux Rate' is a parameter that has some bearing on compression ratio for MPEG
1976 it has a hard to predict effect on the degree of compression */
1977 .TieSpinCtrl(XXO("Mux Rate:"), {wxT("/FileFormats/FFmpegMuxRate"), 0}, 10000000, 0);
1978
1979 S.Id(FEPacketSizeID)
1980 /* i18n-hint: 'Packet Size' is a parameter that has some bearing on compression ratio for MPEG
1981 compression. It measures how big a chunk of audio is compressed in one piece. */
1982 .ToolTip(XO("Packet size\nOptional\n0 - default"))
1983 /* i18n-hint: 'Packet Size' is a parameter that has some bearing on compression ratio for MPEG
1984 compression. It measures how big a chunk of audio is compressed in one piece. */
1985 .TieSpinCtrl(XXO("Packet Size:"), {wxT("/FileFormats/FFmpegPacketSize"), 0}, 10000000, 0);
1986 }
1987 S.EndMultiColumn();
1988 }
1989 S.EndStatic();
1990 //S.EndScroller();
1991 S.SetBorder( 5 );
1992 S.AddStandardButtons(eOkButton | eCancelButton | eHelpButton );
1993 }
1994 S.EndVerticalLay();
1995 }
1996 S.EndMultiColumn();
1997 }
1998 S.EndMultiColumn();
1999 S.EndVerticalLay();
2000
2001 Layout();
2002 Fit();
2003 SetMinSize(GetSize());
2004 Center();
2005
2006 return;
2007 }
2008
2009 ///
2010 ///
FindSelectedFormat(wxString ** name,wxString ** longname)2011 void ExportFFmpegOptions::FindSelectedFormat(wxString **name, wxString **longname)
2012 {
2013 // Get current selection
2014 wxArrayInt selections;
2015 int n = mFormatList->GetSelections(selections);
2016 if (n <= 0) return;
2017
2018 // Get selected format short name
2019 wxString selfmt = mFormatList->GetString(selections[0]);
2020
2021 // Find its index
2022 int nFormat = make_iterator_range( mFormatNames ).index( selfmt );
2023 if (nFormat == wxNOT_FOUND) return;
2024
2025 // Return short name and description
2026 if (name != NULL) *name = &mFormatNames[nFormat];
2027 if (longname != NULL) *longname = &mFormatLongNames[nFormat];
2028 return;
2029 }
2030 ///
2031 ///
FindSelectedCodec(wxString ** name,wxString ** longname)2032 void ExportFFmpegOptions::FindSelectedCodec(wxString **name, wxString **longname)
2033 {
2034 // Get current selection
2035 wxArrayInt selections;
2036 int n = mCodecList->GetSelections(selections);
2037 if (n <= 0) return;
2038
2039 // Get selected codec short name
2040 wxString selcdc = mCodecList->GetString(selections[0]);
2041
2042 // Find its index
2043 int nCodec = make_iterator_range( mCodecNames ).index( selcdc );
2044 if (nCodec == wxNOT_FOUND) return;
2045
2046 // Return short name and description
2047 if (name != NULL) *name = &mCodecNames[nCodec];
2048 if (longname != NULL) *longname = &mCodecLongNames[nCodec];
2049 }
2050
2051 ///
2052 ///
FetchCompatibleCodecList(const wxChar * fmt,AudacityAVCodecID id)2053 int ExportFFmpegOptions::FetchCompatibleCodecList(const wxChar *fmt, AudacityAVCodecID id)
2054 {
2055 const auto ffmpegId = mFFmpeg->GetAVCodecID(id);
2056
2057 // By default assume that id is not in the list
2058 int index = -1;
2059 // By default no codecs are compatible (yet)
2060 mShownCodecNames.clear();
2061 mShownCodecLongNames.clear();
2062 // Clear the listbox
2063 mCodecList->Clear();
2064 // Zero - format is not found at all
2065 int found = 0;
2066 wxString str(fmt);
2067 for (int i = 0; CompatibilityList[i].fmt != NULL; i++)
2068 {
2069 if (str == CompatibilityList[i].fmt)
2070 {
2071 // Format is found in the list
2072 found = 1;
2073 if (CompatibilityList[i].codec.value == AUDACITY_AV_CODEC_ID_NONE)
2074 {
2075 // Format is found in the list and it is compatible with AUDACITY_AV_CODEC_ID_NONE (means that it is compatible to anything)
2076 found = 2;
2077 break;
2078 }
2079 // Find the codec, that is claimed to be compatible
2080 std::unique_ptr<AVCodecWrapper> codec = mFFmpeg->CreateEncoder(mFFmpeg->GetAVCodecID(CompatibilityList[i].codec));
2081 // If it exists, is audio and has encoder
2082 if (codec != NULL && codec->IsAudio() && mFFmpeg->av_codec_is_encoder(codec->GetWrappedValue()))
2083 {
2084 // If it was selected - remember its NEW index
2085 if ((ffmpegId >= 0) && codec->GetId() == ffmpegId)
2086 index = mShownCodecNames.size();
2087
2088 mShownCodecNames.push_back(wxString::FromUTF8(codec->GetName()));
2089 mShownCodecLongNames.push_back(wxString::Format(wxT("%s - %s"),mShownCodecNames.back(),wxString::FromUTF8(codec->GetLongName())));
2090 }
2091 }
2092 }
2093 // All codecs are compatible with this format
2094 if (found == 2)
2095 {
2096 std::unique_ptr<AVCodecWrapper> codec;
2097 while ((codec = mFFmpeg->GetNextCodec(codec.get()))!=NULL)
2098 {
2099 if (codec->IsAudio() && mFFmpeg->av_codec_is_encoder(codec->GetWrappedValue()))
2100 {
2101 // MP2 is broken.
2102 if( codec->GetId() == mFFmpeg->GetAVCodecID(AUDACITY_AV_CODEC_ID_MP2) )
2103 continue;
2104
2105 if (! make_iterator_range( mShownCodecNames )
2106 .contains( wxString::FromUTF8(codec->GetName()) ) )
2107 {
2108 if ((ffmpegId >= 0) && codec->GetId() == ffmpegId)
2109 index = mShownCodecNames.size();
2110
2111 mShownCodecNames.push_back(wxString::FromUTF8(codec->GetName()));
2112 mShownCodecLongNames.push_back(wxString::Format(wxT("%s - %s"),mShownCodecNames.back(),wxString::FromUTF8(codec->GetLongName())));
2113 }
2114 }
2115 }
2116 }
2117 // Format is not found - find format in libavformat and add its default audio codec
2118 // This allows us to provide limited support for NEW formats without modifying the compatibility list
2119 else if (found == 0)
2120 {
2121 wxCharBuffer buf = str.ToUTF8();
2122 auto format = mFFmpeg->GuessOutputFormat(buf, nullptr, nullptr);
2123
2124 if (format != nullptr)
2125 {
2126 auto codec = mFFmpeg->CreateEncoder(format->GetAudioCodec());
2127
2128 if (
2129 codec != nullptr && codec->IsAudio() && mFFmpeg->av_codec_is_encoder(codec->GetWrappedValue()))
2130 {
2131 if ((ffmpegId >= 0) && codec->GetId() == ffmpegId)
2132 index = mShownCodecNames.size();
2133
2134 mShownCodecNames.push_back(wxString::FromUTF8(codec->GetName()));
2135 mShownCodecLongNames.push_back(wxString::Format(wxT("%s - %s"),mShownCodecNames.back(),wxString::FromUTF8(codec->GetLongName())));
2136 }
2137 }
2138 }
2139 // Show NEW codec list
2140 mCodecList->Append(mShownCodecNames);
2141
2142 return index;
2143 }
2144
2145 ///
2146 ///
FetchCompatibleFormatList(AudacityAVCodecID id,wxString * selfmt)2147 int ExportFFmpegOptions::FetchCompatibleFormatList(
2148 AudacityAVCodecID id, wxString* selfmt)
2149 {
2150 int index = -1;
2151 mShownFormatNames.clear();
2152 mShownFormatLongNames.clear();
2153 mFormatList->Clear();
2154 std::unique_ptr<AVOutputFormatWrapper> ofmt;
2155
2156 wxArrayString FromList;
2157 // Find all formats compatible to this codec in compatibility list
2158 for (int i = 0; CompatibilityList[i].fmt != NULL; i++)
2159 {
2160 if (CompatibilityList[i].codec == id || (CompatibilityList[i].codec.value == AUDACITY_AV_CODEC_ID_NONE) )
2161 {
2162 if ((selfmt != NULL) && (*selfmt == CompatibilityList[i].fmt)) index = mShownFormatNames.size();
2163 FromList.push_back(CompatibilityList[i].fmt);
2164 mShownFormatNames.push_back(CompatibilityList[i].fmt);
2165 auto tofmt = mFFmpeg->GuessOutputFormat(
2166 wxString(CompatibilityList[i].fmt).ToUTF8(), nullptr, nullptr);
2167
2168 if (tofmt != NULL)
2169 {
2170 mShownFormatLongNames.push_back(wxString::Format(
2171 wxT("%s - %s"), CompatibilityList[i].fmt,
2172 wxString::FromUTF8(tofmt->GetLongName())));
2173 }
2174 }
2175 }
2176 bool found = false;
2177 if (selfmt != NULL)
2178 {
2179 for (int i = 0; CompatibilityList[i].fmt != NULL; i++)
2180 {
2181 if (*selfmt == CompatibilityList[i].fmt)
2182 {
2183 found = true;
2184 break;
2185 }
2186 }
2187 }
2188 // Format was in the compatibility list
2189 if (found)
2190 {
2191 // Find all formats which have this codec as default and which are not in the list yet and add them too
2192 while ((ofmt = mFFmpeg->GetNextOutputFormat(ofmt.get()))!=NULL)
2193 {
2194 if (ofmt->GetAudioCodec() == mFFmpeg->GetAVCodecID(id))
2195 {
2196 wxString ofmtname = wxString::FromUTF8(ofmt->GetName());
2197 found = false;
2198 for (unsigned int i = 0; i < FromList.size(); i++)
2199 {
2200 if (ofmtname == FromList[i])
2201 {
2202 found = true;
2203 break;
2204 }
2205 }
2206 if (!found)
2207 {
2208 if ((selfmt != NULL) &&
2209 (*selfmt == wxString::FromUTF8(ofmt->GetName())))
2210 index = mShownFormatNames.size();
2211
2212 mShownFormatNames.push_back(wxString::FromUTF8(ofmt->GetName()));
2213
2214 mShownFormatLongNames.push_back(wxString::Format(
2215 wxT("%s - %s"), mShownFormatNames.back(),
2216 wxString::FromUTF8(ofmt->GetLongName())));
2217 }
2218 }
2219 }
2220 }
2221 mFormatList->Append(mShownFormatNames);
2222 return index;
2223 }
2224
2225 ///
2226 ///
OnDeletePreset(wxCommandEvent & WXUNUSED (event))2227 void ExportFFmpegOptions::OnDeletePreset(wxCommandEvent& WXUNUSED(event))
2228 {
2229 wxComboBox *preset = dynamic_cast<wxComboBox*>(FindWindowById(FEPresetID,this));
2230 wxString presetname = preset->GetValue();
2231 if (presetname.empty())
2232 {
2233 AudacityMessageBox( XO("You can't delete a preset without name") );
2234 return;
2235 }
2236
2237 auto query = XO("Delete preset '%s'?").Format( presetname );
2238 int action = AudacityMessageBox(
2239 query,
2240 XO("Confirm Deletion"),
2241 wxYES_NO | wxCENTRE);
2242 if (action == wxNO) return;
2243
2244 mPresets->DeletePreset(presetname);
2245 long index = preset->FindString(presetname);
2246 preset->SetValue(wxEmptyString);
2247 preset->Delete(index);
2248 mPresetNames.erase(
2249 std::find( mPresetNames.begin(), mPresetNames.end(), presetname )
2250 );
2251 }
2252
2253 ///
2254 ///
OnSavePreset(wxCommandEvent & WXUNUSED (event))2255 void ExportFFmpegOptions::OnSavePreset(wxCommandEvent& WXUNUSED(event))
2256 { const bool kCheckForOverwrite = true;
2257 SavePreset(kCheckForOverwrite);
2258 }
2259
2260 // Return false if failed to save.
SavePreset(bool bCheckForOverwrite)2261 bool ExportFFmpegOptions::SavePreset(bool bCheckForOverwrite)
2262 {
2263 wxComboBox *preset = dynamic_cast<wxComboBox*>(FindWindowById(FEPresetID,this));
2264 wxString name = preset->GetValue();
2265 if (name.empty())
2266 {
2267 AudacityMessageBox( XO("You can't save a preset without a name") );
2268 return false;
2269 }
2270 if( bCheckForOverwrite && !mPresets->OverwriteIsOk(name))
2271 return false;
2272 if( !mPresets->SavePreset(this,name) )
2273 return false;
2274 int index = mPresetNames.Index(name,false);
2275 if (index == -1)
2276 {
2277 mPresetNames.push_back(name);
2278 mPresetCombo->Clear();
2279 mPresetCombo->Append(mPresetNames);
2280 mPresetCombo->Select(mPresetNames.Index(name,false));
2281 }
2282 return true;
2283 }
2284
2285 ///
2286 ///
OnLoadPreset(wxCommandEvent & WXUNUSED (event))2287 void ExportFFmpegOptions::OnLoadPreset(wxCommandEvent& WXUNUSED(event))
2288 {
2289 wxComboBox *preset = dynamic_cast<wxComboBox*>(FindWindowById(FEPresetID,this));
2290 wxString presetname = preset->GetValue();
2291
2292 mShownFormatNames = mFormatNames;
2293 mShownFormatLongNames = mFormatLongNames;
2294 mFormatList->Clear();
2295 mFormatList->Append(mFormatNames);
2296
2297 mShownCodecNames = mCodecNames;
2298 mShownCodecLongNames = mCodecLongNames;
2299 mCodecList->Clear();
2300 mCodecList->Append(mCodecNames);
2301
2302 mPresets->LoadPreset(this,presetname);
2303
2304 DoOnFormatList();
2305 DoOnCodecList();
2306 }
2307
FileTypes()2308 static const FileNames::FileTypes &FileTypes()
2309 {
2310 static const FileNames::FileTypes result{
2311 FileNames::XMLFiles, FileNames::AllFiles };
2312 return result;
2313 };
2314
2315 ///
2316 ///
OnImportPresets(wxCommandEvent & WXUNUSED (event))2317 void ExportFFmpegOptions::OnImportPresets(wxCommandEvent& WXUNUSED(event))
2318 {
2319 wxString path;
2320 FileDialogWrapper dlg(this,
2321 XO("Select xml file with presets to import"),
2322 gPrefs->Read(wxT("/FileFormats/FFmpegPresetDir")),
2323 wxEmptyString,
2324 FileTypes(),
2325 wxFD_OPEN);
2326 if (dlg.ShowModal() == wxID_CANCEL) return;
2327 path = dlg.GetPath();
2328 mPresets->ImportPresets(path);
2329 mPresets->GetPresetList(mPresetNames);
2330 mPresetCombo->Clear();
2331 mPresetCombo->Append(mPresetNames);
2332 }
2333
2334 ///
2335 ///
OnExportPresets(wxCommandEvent & WXUNUSED (event))2336 void ExportFFmpegOptions::OnExportPresets(wxCommandEvent& WXUNUSED(event))
2337 {
2338 const bool kCheckForOverwrite = true;
2339 // Bug 1180 save any pending preset before exporting the lot.
2340 // If saving fails, don't try to export.
2341 if( !SavePreset(!kCheckForOverwrite) )
2342 return;
2343
2344 wxArrayString presets;
2345 mPresets->GetPresetList( presets);
2346 if( presets.Count() < 1)
2347 {
2348 AudacityMessageBox( XO("No presets to export") );
2349 return;
2350 }
2351
2352 wxString path;
2353 FileDialogWrapper dlg(this,
2354 XO("Select xml file to export presets into"),
2355 gPrefs->Read(wxT("/FileFormats/FFmpegPresetDir")),
2356 wxEmptyString,
2357 FileTypes(),
2358 wxFD_SAVE|wxFD_OVERWRITE_PROMPT);
2359 if (dlg.ShowModal() == wxID_CANCEL) return;
2360 path = dlg.GetPath();
2361 mPresets->ExportPresets(path);
2362 }
2363
2364 ///
2365 ///
OnAllFormats(wxCommandEvent & WXUNUSED (event))2366 void ExportFFmpegOptions::OnAllFormats(wxCommandEvent& WXUNUSED(event))
2367 {
2368 mShownFormatNames = mFormatNames;
2369 mShownFormatLongNames = mFormatLongNames;
2370 mFormatList->Clear();
2371 mFormatList->Append(mFormatNames);
2372 }
2373
2374 ///
2375 ///
OnAllCodecs(wxCommandEvent & WXUNUSED (event))2376 void ExportFFmpegOptions::OnAllCodecs(wxCommandEvent& WXUNUSED(event))
2377 {
2378 mShownCodecNames = mCodecNames;
2379 mShownCodecLongNames = mCodecLongNames;
2380 mCodecList->Clear();
2381 mCodecList->Append(mCodecNames);
2382 }
2383
2384 /// ReportIfBadCombination will trap
2385 /// bad combinations of format and codec and report
2386 /// using a message box.
2387 /// We may later extend it to catch bad parameters too.
2388 /// @return true iff a bad combination was reported
2389 /// At the moment we don't trap unrecognised format
2390 /// or codec. (We do not expect them to happen ever).
ReportIfBadCombination()2391 bool ExportFFmpegOptions::ReportIfBadCombination()
2392 {
2393 wxString *selcdc = nullptr;
2394 wxString* selcdclong = nullptr;
2395
2396 FindSelectedCodec(&selcdc, &selcdclong);
2397
2398 if (selcdc == nullptr)
2399 return false; // unrecognised codec. Treated as OK
2400
2401 auto cdc = mFFmpeg->CreateEncoder(selcdc->ToUTF8());
2402
2403 if (cdc == nullptr)
2404 return false; // unrecognised codec. Treated as OK
2405
2406 wxString* selfmt = nullptr;
2407 wxString* selfmtlong = nullptr;
2408
2409 FindSelectedFormat(&selfmt, &selfmtlong);
2410
2411 if (selfmt == nullptr)
2412 return false; // unrecognised format; Treated as OK
2413
2414 // This is intended to test for illegal combinations.
2415 // However, the list updating now seems to be working correctly
2416 // making it impossible to select illegal combinations
2417 bool bFound = false;
2418 for (int i = 0; CompatibilityList[i].fmt != NULL; i++)
2419 {
2420 if (*selfmt == CompatibilityList[i].fmt)
2421 {
2422 if (CompatibilityList[i].codec == mFFmpeg->GetAudacityCodecID(cdc->GetId()) || (CompatibilityList[i].codec == AUDACITY_AV_CODEC_ID_NONE) ){
2423 bFound = true;
2424 break;
2425 }
2426 }
2427 }
2428
2429 // We can put extra code in here, to disallow combinations
2430 // We could also test for illegal parameters, and deliver
2431 // custom error messages in that case.
2432 // The below would make AAC codec disallowed.
2433 //if( cdc->id == AUDACITY_AV_CODEC_ID_AAC)
2434 // bFound = false;
2435
2436 // Valid combination was found, so no reporting.
2437 if( bFound )
2438 return false;
2439
2440 AudacityMessageBox(
2441 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
2442 XO("Format %s is not compatible with codec %s.")
2443 .Format( *selfmt, *selcdc ),
2444 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
2445 XO("Incompatible format and codec"));
2446
2447 return true;
2448 }
2449
2450
2451
EnableDisableControls(AVCodecWrapper * cdc,wxString * selfmt)2452 void ExportFFmpegOptions::EnableDisableControls(AVCodecWrapper *cdc, wxString *selfmt)
2453 {
2454 int handled = -1;
2455 for (int i = 0; apptable[i].control != 0; i++)
2456 {
2457 if (apptable[i].control != handled)
2458 {
2459 bool codec = false;
2460 bool format = false;
2461 if (apptable[i].codec == AUDACITY_AV_CODEC_ID_NONE)
2462 {
2463 codec = true;
2464 }
2465 else if (
2466 cdc != NULL &&
2467 apptable[i].codec == mFFmpeg->GetAudacityCodecID(cdc->GetId()))
2468 {
2469 codec = true;
2470 }
2471
2472 if (wxString::FromUTF8(apptable[i].format) == wxT("any")) format = true;
2473 else if (selfmt != NULL &&
2474 *selfmt == wxString::FromUTF8(apptable[i].format)) format = true;
2475 if (codec && format)
2476 {
2477 handled = apptable[i].control;
2478 wxWindow *item = FindWindowById(apptable[i].control,this);
2479 if (item != NULL) item->Enable(apptable[i].enable);
2480 }
2481 }
2482 }
2483 }
2484
DoOnFormatList()2485 void ExportFFmpegOptions::DoOnFormatList()
2486 {
2487 wxString *selfmt = NULL;
2488 wxString *selfmtlong = NULL;
2489 FindSelectedFormat(&selfmt, &selfmtlong);
2490 if (selfmt == NULL)
2491 {
2492 return;
2493 }
2494
2495 wxString *selcdc = NULL;
2496 wxString *selcdclong = NULL;
2497 FindSelectedCodec(&selcdc, &selcdclong);
2498
2499 auto fmt = mFFmpeg->GuessOutputFormat(selfmt->ToUTF8(),NULL,NULL);
2500 if (fmt == NULL)
2501 {
2502 //This shouldn't really happen
2503 mFormatName->SetLabel(wxString(_("Failed to guess format")));
2504 return;
2505 }
2506 mFormatName->SetLabel(wxString::Format(wxT("%s"), *selfmtlong));
2507
2508 AudacityAVCodecID selcdcid = AUDACITY_AV_CODEC_ID_NONE;
2509
2510 if (selcdc != nullptr)
2511 {
2512 auto cdc = mFFmpeg->CreateEncoder(selcdc->ToUTF8());
2513
2514 if (cdc != nullptr)
2515 {
2516 selcdcid = mFFmpeg->GetAudacityCodecID(cdc->GetId());
2517 }
2518 }
2519 int newselcdc =
2520 FetchCompatibleCodecList(*selfmt, selcdcid);
2521
2522 if (newselcdc >= 0) mCodecList->Select(newselcdc);
2523
2524 std::unique_ptr<AVCodecWrapper> cdc;
2525
2526 if (selcdc != nullptr)
2527 cdc = mFFmpeg->CreateEncoder(selcdc->ToUTF8());
2528
2529 EnableDisableControls(cdc.get(), selfmt);
2530 Layout();
2531 Fit();
2532 return;
2533 }
2534
DoOnCodecList()2535 void ExportFFmpegOptions::DoOnCodecList()
2536 {
2537 wxString *selcdc = nullptr;
2538 wxString* selcdclong = nullptr;
2539
2540 FindSelectedCodec(&selcdc, &selcdclong);
2541
2542 if (selcdc == nullptr)
2543 {
2544 return;
2545 }
2546
2547 wxString* selfmt = nullptr;
2548 wxString* selfmtlong = nullptr;
2549
2550 FindSelectedFormat(&selfmt, &selfmtlong);
2551
2552 auto cdc = mFFmpeg->CreateEncoder(selcdc->ToUTF8());
2553 if (cdc == nullptr)
2554 {
2555 //This shouldn't really happen
2556 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
2557 mCodecName->SetLabel(wxString(_("Failed to find the codec")));
2558 return;
2559 }
2560
2561 mCodecName->SetLabel(wxString::Format(wxT("[%d] %s"), (int) mFFmpeg->GetAudacityCodecID(cdc->GetId()).value, *selcdclong));
2562
2563 if (selfmt != nullptr)
2564 {
2565 auto fmt = mFFmpeg->GuessOutputFormat(selfmt->ToUTF8(), nullptr, nullptr);
2566 if (fmt == nullptr)
2567 {
2568 selfmt = nullptr;
2569 selfmtlong = nullptr;
2570 }
2571 }
2572
2573 int newselfmt = FetchCompatibleFormatList(
2574 mFFmpeg->GetAudacityCodecID(cdc->GetId()), selfmt);
2575
2576 if (newselfmt >= 0)
2577 mFormatList->Select(newselfmt);
2578
2579 EnableDisableControls(cdc.get(), selfmt);
2580 Layout();
2581 Fit();
2582 return;
2583 }
2584
2585 ///
2586 ///
OnFormatList(wxCommandEvent & WXUNUSED (event))2587 void ExportFFmpegOptions::OnFormatList(wxCommandEvent& WXUNUSED(event))
2588 {
2589 DoOnFormatList();
2590 }
2591
2592 ///
2593 ///
OnCodecList(wxCommandEvent & WXUNUSED (event))2594 void ExportFFmpegOptions::OnCodecList(wxCommandEvent& WXUNUSED(event))
2595 {
2596 DoOnCodecList();
2597 }
2598
2599
2600 ///
2601 ///
OnOK(wxCommandEvent & WXUNUSED (event))2602 void ExportFFmpegOptions::OnOK(wxCommandEvent& WXUNUSED(event))
2603 {
2604 if( ReportIfBadCombination() )
2605 return;
2606
2607 int selcdc = mCodecList->GetSelection();
2608 int selfmt = mFormatList->GetSelection();
2609 if (selcdc > -1) gPrefs->Write(wxT("/FileFormats/FFmpegCodec"),mCodecList->GetString(selcdc));
2610 if (selfmt > -1) gPrefs->Write(wxT("/FileFormats/FFmpegFormat"),mFormatList->GetString(selfmt));
2611 gPrefs->Flush();
2612
2613 ShuttleGui S(this, eIsSavingToPrefs);
2614 PopulateOrExchange(S);
2615
2616 gPrefs->Flush();
2617
2618 EndModal(wxID_OK);
2619
2620 return;
2621 }
2622
OnGetURL(wxCommandEvent & WXUNUSED (event))2623 void ExportFFmpegOptions::OnGetURL(wxCommandEvent & WXUNUSED(event))
2624 {
2625 HelpSystem::ShowHelp(this, L"Custom_FFmpeg_Export_Options");
2626 }
2627
2628
2629 #endif
2630