Audacity 3.2.0
ExportMP3.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ExportMP3.cpp
6
7 Joshua Haberman
8
9 This just acts as an interface to LAME. A Lame dynamic library must
10 be present
11
12 The difficulty in our approach is that we are attempting to use LAME
13 in a way it was not designed to be used. LAME's API is reasonably
14 consistent, so if we were linking directly against it we could expect
15 this code to work with a variety of different LAME versions. However,
16 the data structures change from version to version, and so linking
17 with one version of the header and dynamically linking against a
18 different version of the dynamic library will not work correctly.
19
20 The solution is to find the lowest common denominator between versions.
21 The bare minimum of functionality we must use is this:
22 1. Initialize the library.
23 2. Set, at minimum, the following global options:
24 i. input sample rate
25 ii. input channels
26 3. Encode the stream
27 4. Call the finishing routine
28
29 Just so that it's clear that we're NOT free to use whatever features
30 of LAME we like, I'm not including lame.h, but instead enumerating
31 here the extent of functions and structures that we can rely on being
32 able to import and use from a dynamic library.
33
34 For the record, we aim to support LAME 3.70 on. Since LAME 3.70 was
35 released in April of 2000, that should be plenty.
36
37
38 Copyright 2002, 2003 Joshua Haberman.
39 Some portions may be Copyright 2003 Paolo Patruno.
40
41 This program is free software; you can redistribute it and/or modify
42 it under the terms of the GNU General Public License as published by
43 the Free Software Foundation; either version 2 of the License, or
44 (at your option) any later version.
45
46 This program is distributed in the hope that it will be useful,
47 but WITHOUT ANY WARRANTY; without even the implied warranty of
48 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
49 GNU General Public License for more details.
50
51 You should have received a copy of the GNU General Public License
52 along with this program; if not, write to the Free Software
53 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
54
55*******************************************************************//********************************************************************/
61
62
63#include "ExportMP3.h"
64
65#include <wx/app.h>
66#include <wx/defs.h>
67
68#include <wx/dynlib.h>
69#include <wx/ffile.h>
70#include <wx/log.h>
71#include <wx/mimetype.h>
72
73#include <wx/textctrl.h>
74#include <wx/choice.h>
75
76#include <rapidjson/document.h>
77
78#include "FileNames.h"
79#include "float_cast.h"
80#include "HelpSystem.h"
81#include "Mix.h"
82#include "Prefs.h"
83#include "Tags.h"
84#include "Track.h"
85#include "wxFileNameWrapper.h"
86#include "wxPanelWrapper.h"
87#include "Project.h"
88
89#include "Export.h"
90#include "BasicUI.h"
91
92#include <lame/lame.h>
93
94#ifdef USE_LIBID3TAG
95#include <id3tag.h>
96#endif
97
98#include "ExportOptionsEditor.h"
99#include "ExportPluginHelpers.h"
100#include "ExportPluginRegistry.h"
101#include "SelectFile.h"
102#include "ShuttleGui.h"
103
104//----------------------------------------------------------------------------
105// ExportMP3Options
106//----------------------------------------------------------------------------
107
108enum : int {
110
111 //ROUTINE_FAST = 0,
112 //ROUTINE_STANDARD = 1,
113
118};
119
120/* i18n-hint: kbps is the bitrate of the MP3 file, kilobits per second*/
121inline TranslatableString n_kbps( int n ){ return XO("%d kbps").Format( n ); }
122
124 n_kbps(320),
125 n_kbps(256),
126 n_kbps(224),
127 n_kbps(192),
128 n_kbps(160),
129 n_kbps(144),
130 n_kbps(128),
131 n_kbps(112),
132 n_kbps(96),
133 n_kbps(80),
134 n_kbps(64),
135 n_kbps(56),
136 n_kbps(48),
137 n_kbps(40),
138 n_kbps(32),
139 n_kbps(24),
140 n_kbps(16),
141 n_kbps(8),
142};
143
144static const std::vector<ExportValue> fixRateValues {
145 320,
146 256,
147 224,
148 192,
149 160,
150 144,
151 128,
152 112,
153 96,
154 80,
155 64,
156 56,
157 48,
158 40,
159 32,
160 24,
161 16,
162 8,
163};
164
166 XO("220-260 kbps (Best Quality)"),
167 XO("200-250 kbps"),
168 XO("170-210 kbps"),
169 XO("155-195 kbps"),
170 XO("145-185 kbps"),
171 XO("110-150 kbps"),
172 XO("95-135 kbps"),
173 XO("80-120 kbps"),
174 XO("65-105 kbps"),
175 XO("45-85 kbps (Smaller files)"),
176};
177/*
178static const TranslatableStrings varModeNames {
179 XO("Fast"),
180 XO("Standard"),
181};
182*/
184 /* i18n-hint: Slightly humorous - as in use an insane precision with MP3.*/
185 XO("Insane, 320 kbps"),
186 XO("Extreme, 220-260 kbps"),
187 XO("Standard, 170-210 kbps"),
188 XO("Medium, 145-185 kbps"),
189};
190
192 /* i18n-hint: Slightly humorous - as in use an insane precision with MP3.*/
193 XO("Insane"),
194 XO("Extreme"),
195 XO("Standard"),
196 XO("Medium"),
197};
198
199static const std::vector< int > sampRates {
200 8000,
201 11025,
202 12000,
203 16000,
204 22050,
205 24000,
206 32000,
207 44100,
208 48000,
209};
210
211enum MP3OptionID : int {
218
219//Option order should exactly match to the id values
220const std::initializer_list<ExportOption> MP3Options {
221 {
222 MP3OptionIDMode, XO("Bit Rate Mode"),
223 std::string("SET"),
225 {
226 // for migrating old preferences the
227 // order should be preserved
228 std::string("SET"),
229 std::string("VBR"),
230 std::string("ABR"),
231 std::string("CBR")
232 },
233 {
234 XO("Preset"),
235 XO("Variable"),
236 XO("Average"),
237 XO("Constant")
238 }
239 },
240 {
241 MP3OptionIDQualitySET, XO("Quality"),
244 { 0, 1, 2, 3 },
246 },
247 {
248 MP3OptionIDQualityVBR, XO("Quality"),
249 QUALITY_2,
251 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 },
253 },
254 {
255 MP3OptionIDQualityABR, XO("Quality"),
256 192,
260 },
261 {
262 MP3OptionIDQualityCBR, XO("Quality"),
263 192,
267 }
268};
269
271{
272 std::vector<ExportOption> mOptions;
273 std::unordered_map<int, ExportValue> mValues;
275public:
276
279 , mListener(listener)
280 {
281 mValues.reserve(mOptions.size());
282 for(auto& option : mOptions)
283 mValues[option.id] = option.defaultValue;
284 }
285
286 int GetOptionsCount() const override
287 {
288 return static_cast<int>(mOptions.size());
289 }
290
291 bool GetOption(int index, ExportOption& option) const override
292 {
293 if(index >= 0 && index < static_cast<int>(mOptions.size()))
294 {
295 option = mOptions[index];
296 return true;
297 }
298 return false;
299 }
300
301 bool SetValue(int id, const ExportValue& value) override
302 {
303 const auto it = mValues.find(id);
304 if(it == mValues.end())
305 return false;
306 if(value.index() != it->second.index())
307 return false;
308
309 it->second = value;
310
311 switch(id)
312 {
313 case MP3OptionIDMode:
314 {
315 const auto mode = *std::get_if<std::string>(&value);
316 OnModeChange(mode);
317 if(mListener)
318 {
325
327 }
328 } break;
333 {
334 if(mListener)
336 } break;
337 default: break;
338 }
339 return true;
340 }
341
342 bool GetValue(int id, ExportValue& value) const override
343 {
344 const auto it = mValues.find(id);
345 if(it != mValues.end())
346 {
347 value = it->second;
348 return true;
349 }
350 return false;
351 }
352
354 {
355 // Retrieve preferences
356 int highrate = 48000;
357 int lowrate = 8000;
358
359 const auto rmode = *std::get_if<std::string>(&mValues.find(MP3OptionIDMode)->second);
360
361 if (rmode == "ABR") {
362 auto bitrate = *std::get_if<int>(&mValues.find(MP3OptionIDQualityABR)->second);
363 if (bitrate > 160) {
364 lowrate = 32000;
365 }
366 else if (bitrate < 32 || bitrate == 144) {
367 highrate = 24000;
368 }
369 }
370 else if (rmode == "CBR") {
371 auto bitrate = *std::get_if<int>(&mValues.find(MP3OptionIDQualityCBR)->second);
372
373 if (bitrate > 160) {
374 lowrate = 32000;
375 }
376 else if (bitrate < 32 || bitrate == 144) {
377 highrate = 24000;
378 }
379 }
380
381 SampleRateList result;
382 result.reserve(sampRates.size());
383 for(auto rate : sampRates)
384 if(rate >= lowrate && rate <= highrate)
385 result.push_back(rate);
386
387 return result;
388 }
389
390 void Load(const audacity::BasicSettings& config) override
391 {
392 wxString mode;
393 if(config.Read(wxT("/FileFormats/MP3RateModeChoice"), &mode))
394 mValues[MP3OptionIDMode] = mode.ToStdString();
395 else
396 {
397 //attempt to recover from old-style preference
398 int index;
399 if(config.Read(wxT("/FileFormats/MP3RateMode"), &index))
401 }
402
403 config.Read(wxT("/FileFormats/MP3SetRate"), std::get_if<int>(&mValues[MP3OptionIDQualitySET]));
404 config.Read(wxT("/FileFormats/MP3AbrRate"), std::get_if<int>(&mValues[MP3OptionIDQualityABR]));
405 config.Read(wxT("/FileFormats/MP3CbrRate"), std::get_if<int>(&mValues[MP3OptionIDQualityCBR]));
406 config.Read(wxT("/FileFormats/MP3VbrRate"), std::get_if<int>(&mValues[MP3OptionIDQualityVBR]));
407
408 OnModeChange(*std::get_if<std::string>(&mValues[MP3OptionIDMode]));
409 }
410
411 void Store(audacity::BasicSettings& config) const override
412 {
413 auto it = mValues.find(MP3OptionIDMode);
414 config.Write(wxT("/FileFormats/MP3RateModeChoice"), wxString(*std::get_if<std::string>(&it->second)));
415
417 config.Write(wxT("/FileFormats/MP3SetRate"), *std::get_if<int>(&it->second));
419 config.Write(wxT("/FileFormats/MP3AbrRate"), *std::get_if<int>(&it->second));
421 config.Write(wxT("/FileFormats/MP3CbrRate"), *std::get_if<int>(&it->second));
423 config.Write(wxT("/FileFormats/MP3VbrRate"), *std::get_if<int>(&it->second));
424 }
425
426private:
427
428 void OnModeChange(const std::string& mode)
429 {
434
435 if(mode == "SET")
437 else if(mode == "ABR")
439 else if(mode == "CBR")
441 else if(mode == "VBR")
443 }
444};
445
446namespace {
447
448int ValidateValue( int nValues, int value, int defaultValue )
449{
450 return (value >= 0 && value < nValues) ? value : defaultValue;
451}
452
453int ValidateValue( const std::vector<int> &values, int value, int defaultValue )
454{
455 auto start = values.begin(), finish = values.end(),
456 iter = std::find( start, finish, value );
457 return ( iter != finish ) ? value : defaultValue;
458}
459
460int ValidateIndex( const std::vector<int> &values, int value, int defaultIndex )
461{
462 auto start = values.begin(), finish = values.end(),
463 iter = std::find( start, finish, value );
464 return ( iter != finish ) ? static_cast<int>( iter - start ) : defaultIndex;
465}
466
467}
468
469//----------------------------------------------------------------------------
470// FindDialog
471//----------------------------------------------------------------------------
472
473#define ID_BROWSE 5000
474#define ID_DLOAD 5001
475
476class FindDialog final : public wxDialogWrapper
477{
478public:
479
480#ifndef DISABLE_DYNAMIC_LOADING_LAME
481
482 FindDialog(wxWindow *parent, wxString path, wxString name,
484 : wxDialogWrapper(parent, wxID_ANY,
485 /* i18n-hint: LAME is the name of an MP3 converter and should not be translated*/
486 XO("Locate LAME"))
487 {
488 SetName();
489 ShuttleGui S(this, eIsCreating);
490
491 mPath = path;
492 mName = name;
493 mTypes = std::move( types );
494
495 mLibPath.Assign(mPath, mName);
496
498 }
499
501 {
502 S.SetBorder(10);
503 S.StartVerticalLay(true);
504 {
505 S.AddTitle(
506 XO("Audacity needs the file %s to create MP3s.")
507 .Format( mName ) );
508
509 S.SetBorder(3);
510 S.StartHorizontalLay(wxALIGN_LEFT, true);
511 {
512 S.AddTitle( XO("Location of %s:").Format( mName ) );
513 }
514 S.EndHorizontalLay();
515
516 S.StartMultiColumn(2, wxEXPAND);
517 S.SetStretchyCol(0);
518 {
519 if (mLibPath.GetFullPath().empty()) {
520 mPathText = S.AddTextBox( {},
521 /* i18n-hint: There is a button to the right of the arrow.*/
522 wxString::Format(_("To find %s, click here -->"), mName), 0);
523 }
524 else {
525 mPathText = S.AddTextBox( {}, mLibPath.GetFullPath(), 0);
526 }
527 S.Id(ID_BROWSE).AddButton(XXO("Browse..."), wxALIGN_RIGHT);
528 S.AddVariableText(
529 /* i18n-hint: There is a button to the right of the arrow.*/
530 XO("To get a free copy of LAME, click here -->"), true);
531 /* i18n-hint: (verb)*/
532 S.Id(ID_DLOAD).AddButton(XXO("Download"), wxALIGN_RIGHT);
533 }
534 S.EndMultiColumn();
535
536 S.AddStandardButtons();
537 }
538 S.EndVerticalLay();
539
540 Layout();
541 Fit();
542 SetMinSize(GetSize());
543 Center();
544
545 return;
546 }
547
548 void OnBrowse(wxCommandEvent & WXUNUSED(event))
549 {
550 /* i18n-hint: It's asking for the location of a file, for
551 * example, "Where is lame_enc.dll?" - you could translate
552 * "Where would I find the file %s" instead if you want. */
553 auto question = XO("Where is %s?").Format( mName );
554
555 wxString path = SelectFile(FileNames::Operation::_None,
556 question,
557 mLibPath.GetPath(),
558 mLibPath.GetName(),
559 wxT(""),
560 mTypes,
561 wxFD_OPEN | wxRESIZE_BORDER,
562 this);
563 if (!path.empty()) {
564 mLibPath = path;
565 mPathText->SetValue(path);
566 }
567 }
568
569 void OnDownload(wxCommandEvent & WXUNUSED(event))
570 {
571 HelpSystem::ShowHelp(this, L"FAQ:Installing_the_LAME_MP3_Encoder");
572 }
573
574 wxString GetLibPath()
575 {
576 return mLibPath.GetFullPath();
577 }
578
579#endif // DISABLE_DYNAMIC_LOADING_LAME
580
581private:
582
583#ifndef DISABLE_DYNAMIC_LOADING_LAME
584 wxFileName mLibPath;
585
586 wxString mPath;
587 wxString mName;
589#endif // DISABLE_DYNAMIC_LOADING_LAME
590
591 wxTextCtrl *mPathText;
592
593 DECLARE_EVENT_TABLE()
594};
595
596#ifndef DISABLE_DYNAMIC_LOADING_LAME
597BEGIN_EVENT_TABLE(FindDialog, wxDialogWrapper)
601#endif // DISABLE_DYNAMIC_LOADING_LAME
602
603//----------------------------------------------------------------------------
604// MP3Exporter
605//----------------------------------------------------------------------------
606
607#ifndef DISABLE_DYNAMIC_LOADING_LAME
608
611typedef const char* get_lame_version_t(void);
612
614 lame_t gfp,
615 const float pcm_l[],
616 const float pcm_r[],
617 const int nsamples,
618 unsigned char * mp3buf,
619 const int mp3buf_size);
620
622 lame_t gfp,
623 const float pcm[],
624 const int nsamples,
625 unsigned char * mp3buf,
626 const int mp3buf_size);
627
630 unsigned char* mp3buf,
631 int size );
632
634
640typedef int lame_set_VBR_t(lame_global_flags *, vbr_mode);
643typedef int lame_set_mode_t(lame_global_flags *, MPEG_mode);
648typedef size_t lame_get_lametag_frame_t(const lame_global_flags *, unsigned char* buffer, size_t size);
650
651#endif // DISABLE_DYNAMIC_LOADING_LAME
652
653#if defined(__WXMSW__)
654// An alternative solution to give Windows an additional chance of writing the tag before
655// falling bato to lame_mp3_tag_fid(). The latter can have DLL sharing issues when mixing
656// Debug/Release builds of Audacity and the lame DLL.
657typedef unsigned long beWriteInfoTag_t(lame_global_flags *, char *);
658
659// We use this to determine if the user has selected an older, Blade API only, lame_enc.dll
660// so we can be more specific about why their library isn't acceptable.
661typedef struct {
662
663 // BladeEnc DLL Version number
664
667
668 // BladeEnc Engine Version Number
669
672
673 // DLL Release date
674
675 BYTE byDay;
677 WORD wYear;
678
679 // BladeEnc Homepage URL
680
681 CHAR zHomepage[129];
682
686
687 BYTE btReserved[125];
688} be_version;
689typedef void beVersion_t(be_version *);
690#endif
691
693{
694public:
696 {
699 Yes
700 };
701
702 MP3Exporter();
703 ~MP3Exporter();
704
705#ifndef DISABLE_DYNAMIC_LOADING_LAME
706 bool FindLibrary(wxWindow *parent);
707 bool LoadLibrary(wxWindow *parent, AskUser askuser);
708 bool ValidLibraryLoaded();
709#endif // DISABLE_DYNAMIC_LOADING_LAME
710
711 /* These global settings keep state over the life of the object */
712 void SetMode(int mode);
713 void SetBitrate(int rate);
714 void SetQuality(int q/*, int r*/);
715
716 /* Virtual methods that must be supplied by library interfaces */
717
718 /* initialize the library interface */
719 bool InitLibrary(wxString libpath);
720 bool InitLibraryInternal();
721 bool InitLibraryExternal(wxString libpath);
722 void FreeLibrary();
723
724 /* get library info */
725 wxString GetLibraryVersion();
726 wxString GetLibraryName();
727 wxString GetLibraryPath();
729
730 /* returns the number of samples PER CHANNEL to send for each call to EncodeBuffer */
731 int InitializeStream(unsigned channels, int sampleRate);
732
733 /* In bytes. must be called AFTER InitializeStream */
734 int GetOutBufferSize();
735
736 /* returns the number of bytes written. input is interleaved if stereo*/
737 int EncodeBuffer(float inbuffer[], unsigned char outbuffer[]);
738 int EncodeRemainder(float inbuffer[], int nSamples,
739 unsigned char outbuffer[]);
740
741 int EncodeBufferMono(float inbuffer[], unsigned char outbuffer[]);
742 int EncodeRemainderMono(float inbuffer[], int nSamples,
743 unsigned char outbuffer[]);
744
745 int FinishStream(unsigned char outbuffer[]);
746 void CancelEncoding();
747
748 bool PutInfoTag(wxFFile & f, wxFileOffset off);
749
750private:
752
753#ifndef DISABLE_DYNAMIC_LOADING_LAME
754 wxString mLibPath;
755 wxDynamicLibrary lame_lib;
757#endif // DISABLE_DYNAMIC_LOADING_LAME
758
759#if defined(__WXMSW__)
761#endif
762
764 int mMode;
767 //int mRoutine;
768
769#ifndef DISABLE_DYNAMIC_LOADING_LAME
770 /* function pointers to the symbols we get from the library */
778
794#if defined(__WXMSW__)
797#endif
798#endif // DISABLE_DYNAMIC_LOADING_LAME
799
801
802 static const int mSamplesPerChunk = 220500;
803 // See lame.h/lame_encode_buffer() for further explanation
804 // As coded here, this should be the worst case.
805 static const int mOutBufferSize =
806 mSamplesPerChunk * (320 / 8) / 8 + 4 * 1152 * (320 / 8) / 8 + 512;
807
808 // See MAXFRAMESIZE in libmp3lame/VbrTag.c for explanation of 2880.
809 unsigned char mInfoTagBuf[2880];
811};
812
814{
815// We could use #defines rather than this variable.
816// The idea of the variable is that if we wanted, we could allow
817// a dynamic override of the library, e.g. with a newer faster version,
818// or to fix CVEs in the underlying library.
819// for now though the 'variable' is a constant.
820#ifdef MP3_EXPORT_BUILT_IN
821 mLibIsExternal = false;
822#else
823 mLibIsExternal = true;
824#endif
825
826#ifndef DISABLE_DYNAMIC_LOADING_LAME
827 mLibraryLoaded = false;
828#endif // DISABLE_DYNAMIC_LOADING_LAME
829 mEncoding = false;
830 mGF = NULL;
831
832#ifndef DISABLE_DYNAMIC_LOADING_LAME
833 if (gPrefs) {
834 mLibPath = gPrefs->Read(wxT("/MP3/MP3LibPath"), wxT(""));
835 }
836#endif // DISABLE_DYNAMIC_LOADING_LAME
837
838 mBitrate = 128;
840 mMode = MODE_CBR;
841 //mRoutine = ROUTINE_FAST;
842}
843
845{
846 FreeLibrary();
847}
848
849#ifndef DISABLE_DYNAMIC_LOADING_LAME
850
851bool MP3Exporter::FindLibrary(wxWindow *parent)
852{
853 wxString path;
854 wxString name;
855
856 if (!mLibPath.empty()) {
857 wxFileName fn = mLibPath;
858 path = fn.GetPath();
859 name = fn.GetFullName();
860 }
861 else {
862 path = GetLibraryPath();
864 }
865
866 FindDialog fd(parent,
867 path,
868 name,
870
871 if (fd.ShowModal() == wxID_CANCEL) {
872 return false;
873 }
874
875 path = fd.GetLibPath();
876
877 if (!::wxFileExists(path)) {
878 return false;
879 }
880
881 mLibPath = path;
882
883 return (gPrefs->Write(wxT("/MP3/MP3LibPath"), mLibPath) && gPrefs->Flush());
884}
885
886bool MP3Exporter::LoadLibrary(wxWindow *parent, AskUser askuser)
887{
888
889 if (ValidLibraryLoaded()) {
890 FreeLibrary();
891 mLibraryLoaded = false;
892 }
893
894#if defined(__WXMSW__)
895 mBladeVersion = {};
896#endif
897
898 if( !mLibIsExternal ){
900 return mLibraryLoaded;
901 }
902
903 // First try loading it from a previously located path
904 if (!mLibPath.empty()) {
905 wxLogMessage(wxT("Attempting to load LAME from previously defined path"));
907 }
908
909 // If not successful, try loading using system search paths
910 if (!ValidLibraryLoaded()) {
911 wxLogMessage(wxT("Attempting to load LAME from system search paths"));
914 }
915
916 // If not successful, try loading using compiled in path
917 if (!ValidLibraryLoaded()) {
918 wxLogMessage(wxT("Attempting to load LAME from builtin path"));
919 wxFileName fn(GetLibraryPath(), GetLibraryName());
920 mLibPath = fn.GetFullPath();
922 }
923
924 // If not successful, must ask the user
925 if (!ValidLibraryLoaded()) {
926 wxLogMessage(wxT("(Maybe) ask user for library"));
927 if (askuser == MP3Exporter::Maybe && FindLibrary(parent)) {
929 }
930 }
931
932 // Oh well, just give up
933 if (!ValidLibraryLoaded()) {
934#if defined(__WXMSW__)
935 if (askuser && !mBladeVersion.empty()) {
937 }
938#endif
939 wxLogMessage(wxT("Failed to locate LAME library"));
940
941 return false;
942 }
943
944 wxLogMessage(wxT("LAME library successfully loaded"));
945
946 return true;
947}
948
950{
951 return mLibraryLoaded;
952}
953
954#endif // DISABLE_DYNAMIC_LOADING_LAME
955
957{
958 mMode = mode;
959}
960
962{
963 mBitrate = rate;
964}
965
966void MP3Exporter::SetQuality(int q/*, int r*/)
967{
968 mQuality = q;
969}
970
971bool MP3Exporter::InitLibrary(wxString libpath)
972{
974}
975
977{
978 wxLogMessage(wxT("Using internal LAME"));
979
980// The global ::lame_something symbols only exist if LAME is built in.
981// So we don't reference them unless they are.
982#ifdef MP3_EXPORT_BUILT_IN
983
991
1005
1006 // These are optional
1007 //lame_get_lametag_frame = ::lame_get_lametag_frame;
1010
1011#if defined(__WXMSW__)
1012 //beWriteInfoTag = ::beWriteInfoTag;
1013 //beVersion = ::beVersion;
1014 beWriteInfoTag = NULL;
1015 beVersion = NULL;
1016#endif
1017
1018 mGF = lame_init();
1019 if (mGF == NULL) {
1020 return false;
1021 }
1022#endif
1023
1024 return true;
1025}
1026
1027
1029{
1030 wxLogMessage(wxT("Loading LAME from %s"), libpath);
1031
1032#ifndef DISABLE_DYNAMIC_LOADING_LAME
1033 if (!lame_lib.Load(libpath, wxDL_LAZY)) {
1034 wxLogMessage(wxT("load failed"));
1035 return false;
1036 }
1037
1038 wxLogMessage(wxT("Actual LAME path %s"),
1039 FileNames::PathFromAddr(lame_lib.GetSymbol(wxT("lame_init"))));
1040
1042 lame_lib.GetSymbol(wxT("lame_init"));
1044 lame_lib.GetSymbol(wxT("get_lame_version"));
1046 lame_lib.GetSymbol(wxT("lame_init_params"));
1048 lame_lib.GetSymbol(wxT("lame_encode_buffer_ieee_float"));
1050 lame_lib.GetSymbol(wxT("lame_encode_buffer_interleaved_ieee_float"));
1052 lame_lib.GetSymbol(wxT("lame_encode_flush"));
1054 lame_lib.GetSymbol(wxT("lame_close"));
1055
1057 lame_lib.GetSymbol(wxT("lame_set_in_samplerate"));
1059 lame_lib.GetSymbol(wxT("lame_set_out_samplerate"));
1061 lame_lib.GetSymbol(wxT("lame_set_num_channels"));
1063 lame_lib.GetSymbol(wxT("lame_set_quality"));
1065 lame_lib.GetSymbol(wxT("lame_set_brate"));
1067 lame_lib.GetSymbol(wxT("lame_set_VBR"));
1069 lame_lib.GetSymbol(wxT("lame_set_VBR_q"));
1071 lame_lib.GetSymbol(wxT("lame_set_VBR_min_bitrate_kbps"));
1073 lame_lib.GetSymbol(wxT("lame_set_mode"));
1075 lame_lib.GetSymbol(wxT("lame_set_preset"));
1077 lame_lib.GetSymbol(wxT("lame_set_error_protection"));
1079 lame_lib.GetSymbol(wxT("lame_set_disable_reservoir"));
1081 lame_lib.GetSymbol(wxT("lame_set_bWriteVbrTag"));
1082
1083 // These are optional
1085 lame_lib.GetSymbol(wxT("lame_get_lametag_frame"));
1087 lame_lib.GetSymbol(wxT("lame_mp3_tags_fid"));
1088#if defined(__WXMSW__)
1090 lame_lib.GetSymbol(wxT("beWriteInfoTag"));
1092 lame_lib.GetSymbol(wxT("beVersion"));
1093#endif
1094
1095 if (!lame_init ||
1101 !lame_close ||
1106 !lame_set_brate ||
1107 !lame_set_VBR ||
1108 !lame_set_VBR_q ||
1109 !lame_set_mode ||
1110 !lame_set_preset ||
1114 {
1115 wxLogMessage(wxT("Failed to find a required symbol in the LAME library."));
1116#if defined(__WXMSW__)
1117 if (beVersion) {
1118 be_version v;
1119 beVersion(&v);
1120
1121 mBladeVersion = XO(
1122"You are linking to lame_enc.dll v%d.%d. This version is not compatible with Audacity %d.%d.%d.\nPlease download the latest version of 'LAME for Audacity'.")
1123 .Format(
1126 AUDACITY_VERSION,
1127 AUDACITY_RELEASE,
1128 AUDACITY_REVISION);
1129 }
1130#endif
1131
1132 lame_lib.Unload();
1133 return false;
1134 }
1135#endif // DISABLE_DYNAMIC_LOADING_LAME
1136
1137 mGF = lame_init();
1138 if (mGF == NULL) {
1139 return false;
1140 }
1141
1142 return true;
1143}
1144
1146{
1147 if (mGF) {
1148 lame_close(mGF);
1149 mGF = NULL;
1150 }
1151
1152#ifndef DISABLE_DYNAMIC_LOADING_LAME
1153 lame_lib.Unload();
1154#endif // DISABLE_DYNAMIC_LOADING_LAME
1155
1156 return;
1157}
1158
1160{
1161#ifndef DISABLE_DYNAMIC_LOADING_LAME
1162 if (!mLibraryLoaded) {
1163 return wxT("");
1164 }
1165#endif // DISABLE_DYNAMIC_LOADING_LAME
1166
1167 return wxString::Format(wxT("LAME %hs"), get_lame_version());
1168}
1169
1171{
1172#ifndef DISABLE_DYNAMIC_LOADING_LAME
1173 if (!mLibraryLoaded) {
1174 return -1;
1175 }
1176#endif // DISABLE_DYNAMIC_LOADING_LAME
1177
1178 if (channels > 2) {
1179 return -1;
1180 }
1181
1183 lame_set_num_channels(mGF, channels);
1187 // Add the VbrTag for all types. For ABR/VBR, a Xing tag will be created.
1188 // For CBR, it will be a Lame Info tag.
1190
1191 // Set the VBR quality or ABR/CBR bitrate
1192 switch (mMode) {
1193 case MODE_SET:
1194 {
1195 int preset;
1196
1197 if (mQuality == PRESET_INSANE) {
1198 preset = INSANE;
1199 }
1200 //else if (mRoutine == ROUTINE_FAST) {
1201 else if (mQuality == PRESET_EXTREME) {
1202 preset = EXTREME_FAST;
1203 }
1204 else if (mQuality == PRESET_STANDARD) {
1205 preset = STANDARD_FAST;
1206 }
1207 else {
1208 preset = 1007; // Not defined until 3.96
1209 }
1210 //}
1211 /*
1212 else {
1213 if (mQuality == PRESET_EXTREME) {
1214 preset = EXTREME;
1215 }
1216 else if (mQuality == PRESET_STANDARD) {
1217 preset = STANDARD;
1218 }
1219 else {
1220 preset = 1006; // Not defined until 3.96
1221 }
1222 }
1223 */
1225 }
1226 break;
1227
1228 case MODE_VBR:
1229 lame_set_VBR(mGF, vbr_mtrh );
1231 break;
1232
1233 case MODE_ABR:
1235 break;
1236
1237 default:
1238 lame_set_VBR(mGF, vbr_off);
1240 break;
1241 }
1242
1243 // Set the channel mode
1244 MPEG_mode mode;
1245
1246 if (channels == 1)
1247 mode = MONO;
1248 else
1249 mode = JOINT_STEREO;
1250
1251 lame_set_mode(mGF, mode);
1252
1253 int rc = lame_init_params(mGF);
1254 if (rc < 0) {
1255 return rc;
1256 }
1257
1258#if 0
1259 dump_config(mGF);
1260#endif
1261
1262 mInfoTagLen = 0;
1263 mEncoding = true;
1264
1265 return mSamplesPerChunk;
1266}
1267
1269{
1270 if (!mEncoding)
1271 return -1;
1272
1273 return mOutBufferSize;
1274}
1275
1276int MP3Exporter::EncodeBuffer(float inbuffer[], unsigned char outbuffer[])
1277{
1278 if (!mEncoding) {
1279 return -1;
1280 }
1281
1283 outbuffer, mOutBufferSize);
1284}
1285
1286int MP3Exporter::EncodeRemainder(float inbuffer[], int nSamples,
1287 unsigned char outbuffer[])
1288{
1289 if (!mEncoding) {
1290 return -1;
1291 }
1292
1293 return lame_encode_buffer_interleaved_ieee_float(mGF, inbuffer, nSamples, outbuffer,
1295}
1296
1297int MP3Exporter::EncodeBufferMono(float inbuffer[], unsigned char outbuffer[])
1298{
1299 if (!mEncoding) {
1300 return -1;
1301 }
1302
1303 return lame_encode_buffer_ieee_float(mGF, inbuffer,inbuffer, mSamplesPerChunk,
1304 outbuffer, mOutBufferSize);
1305}
1306
1307int MP3Exporter::EncodeRemainderMono(float inbuffer[], int nSamples,
1308 unsigned char outbuffer[])
1309{
1310 if (!mEncoding) {
1311 return -1;
1312 }
1313
1314 return lame_encode_buffer_ieee_float(mGF, inbuffer, inbuffer, nSamples, outbuffer,
1316}
1317
1318int MP3Exporter::FinishStream(unsigned char outbuffer[])
1319{
1320 if (!mEncoding) {
1321 return -1;
1322 }
1323
1324 mEncoding = false;
1325
1326 int result = lame_encode_flush(mGF, outbuffer, mOutBufferSize);
1327
1328#if defined(DISABLE_DYNAMIC_LOADING_LAME)
1330#else
1333 }
1334#endif
1335
1336 return result;
1337}
1338
1340{
1341 mEncoding = false;
1342}
1343
1344bool MP3Exporter::PutInfoTag(wxFFile & f, wxFileOffset off)
1345{
1346 if (mGF) {
1347 if (mInfoTagLen > 0) {
1348 // FIXME: TRAP_ERR Seek and writ ein MP3 exporter could fail.
1349 if ( !f.Seek(off, wxFromStart))
1350 return false;
1351 if (mInfoTagLen > f.Write(mInfoTagBuf, mInfoTagLen))
1352 return false;
1353 }
1354#if defined(__WXMSW__)
1355 else if (beWriteInfoTag) {
1356 if ( !f.Flush() )
1357 return false;
1358 // PRL: What is the correct error check on the return value?
1359 beWriteInfoTag(mGF, OSOUTPUT(f.GetName()));
1360 mGF = NULL;
1361 }
1362#endif
1363 else if (lame_mp3_tags_fid != NULL) {
1364 lame_mp3_tags_fid(mGF, f.fp());
1365 }
1366 }
1367
1368 if ( !f.SeekEnd() )
1369 return false;
1370
1371 return true;
1372}
1373
1374#if defined(__WXMSW__)
1375/* values for Windows */
1376
1378{
1379 wxRegKey reg(wxT("HKEY_LOCAL_MACHINE\\Software\\Lame for Audacity"));
1380 wxString path;
1381
1382 if (reg.Exists()) {
1383 wxLogMessage(wxT("LAME registry key exists."));
1384 reg.QueryValue(wxT("InstallPath"), path);
1385 }
1386 else {
1387 wxLogMessage(wxT("LAME registry key does not exist."));
1388 }
1389
1390 wxLogMessage(wxT("Library path is: ") + path);
1391
1392 return path;
1393}
1394
1396{
1397 return wxT("lame_enc.dll");
1398}
1399
1401{
1402 return {
1403 { XO("Only lame_enc.dll"), { wxT("lame_enc.dll") } },
1406 };
1407}
1408
1409#elif defined(__WXMAC__)
1410/* values for Mac OS X */
1411
1413{
1414 wxString path;
1415
1416 path = wxT("/Library/Application Support/audacity/libs");
1417 if (wxFileExists(path + wxT("/") + GetLibraryName()))
1418 {
1419 return path;
1420 }
1421
1422 path = wxT("/usr/local/lib/audacity");
1423 if (wxFileExists(path + wxT("/") + GetLibraryName()))
1424 {
1425 return path;
1426 }
1427
1428 return wxT("/Library/Application Support/audacity/libs");
1429}
1430
1432{
1433 if (sizeof(void*) == 8)
1434 return wxT("libmp3lame64bit.dylib");
1435 return wxT("libmp3lame.dylib");
1436}
1437
1439{
1440 return {
1441 (sizeof(void*) == 8)
1443 XO("Only libmp3lame64bit.dylib"), { wxT("libmp3lame64bit.dylib") }
1444 }
1446 XO("Only libmp3lame.dylib"), { wxT("libmp3lame.dylib") }
1447 }
1448 ,
1451 };
1452}
1453
1454#elif defined(__OpenBSD__)
1455/* Values for OpenBSD systems */
1456
1458{
1459 return wxT(LIBDIR);
1460}
1461
1463{
1464 return wxT("libmp3lame.so");
1465}
1466
1468{
1469 return {
1470 { XO("Only libmp3lame.so"), { wxT("libmp3lame.so") } },
1471 { XO("Primary shared object files"), { wxT("so") }, true },
1472 { XO("Extended libraries"), { wxT("so*") }, true },
1474 };
1475}
1476
1477#else
1478/* Values for Linux / Unix systems */
1479
1481{
1482 return wxT(LIBDIR);
1483}
1484
1486{
1487 return wxT("libmp3lame.so.0");
1488}
1489
1491{
1492 return {
1493 { XO("Only libmp3lame.so.0"), { wxT("libmp3lame.so.0") } },
1494 { XO("Primary shared object files"), { wxT("so") }, true },
1495 { XO("Extended libraries"), { wxT("so*") }, true },
1497 };
1498}
1499#endif
1500
1501#if 0
1502// Debug routine from BladeMP3EncDLL.c in the libmp3lame distro
1503static void dump_config( lame_global_flags* gfp )
1504{
1505 wxPrintf(wxT("\n\nLame_enc configuration options:\n"));
1506 wxPrintf(wxT("==========================================================\n"));
1507
1508 wxPrintf(wxT("version =%d\n"),lame_get_version( gfp ) );
1509 wxPrintf(wxT("Layer =3\n"));
1510 wxPrintf(wxT("mode ="));
1511 switch ( lame_get_mode( gfp ) )
1512 {
1513 case STEREO: wxPrintf(wxT( "Stereo\n" )); break;
1514 case JOINT_STEREO: wxPrintf(wxT( "Joint-Stereo\n" )); break;
1515 case DUAL_CHANNEL: wxPrintf(wxT( "Forced Stereo\n" )); break;
1516 case MONO: wxPrintf(wxT( "Mono\n" )); break;
1517 case NOT_SET: /* FALLTHROUGH */
1518 default: wxPrintf(wxT( "Error (unknown)\n" )); break;
1519 }
1520
1521 wxPrintf(wxT("Input sample rate =%.1f kHz\n"), lame_get_in_samplerate( gfp ) /1000.0 );
1522 wxPrintf(wxT("Output sample rate =%.1f kHz\n"), lame_get_out_samplerate( gfp ) /1000.0 );
1523
1524 wxPrintf(wxT("bitrate =%d kbps\n"), lame_get_brate( gfp ) );
1525 wxPrintf(wxT("Quality Setting =%d\n"), lame_get_quality( gfp ) );
1526
1527 wxPrintf(wxT("Low pass frequency =%d\n"), lame_get_lowpassfreq( gfp ) );
1528 wxPrintf(wxT("Low pass width =%d\n"), lame_get_lowpasswidth( gfp ) );
1529
1530 wxPrintf(wxT("High pass frequency =%d\n"), lame_get_highpassfreq( gfp ) );
1531 wxPrintf(wxT("High pass width =%d\n"), lame_get_highpasswidth( gfp ) );
1532
1533 wxPrintf(wxT("No short blocks =%d\n"), lame_get_no_short_blocks( gfp ) );
1534 wxPrintf(wxT("Force short blocks =%d\n"), lame_get_force_short_blocks( gfp ) );
1535
1536 wxPrintf(wxT("de-emphasis =%d\n"), lame_get_emphasis( gfp ) );
1537 wxPrintf(wxT("private flag =%d\n"), lame_get_extension( gfp ) );
1538
1539 wxPrintf(wxT("copyright flag =%d\n"), lame_get_copyright( gfp ) );
1540 wxPrintf(wxT("original flag =%d\n"), lame_get_original( gfp ) );
1541 wxPrintf(wxT("CRC =%s\n"), lame_get_error_protection( gfp ) ? wxT("on") : wxT("off") );
1542 wxPrintf(wxT("Fast mode =%s\n"), ( lame_get_quality( gfp ) )? wxT("enabled") : wxT("disabled") );
1543 wxPrintf(wxT("Force mid/side stereo =%s\n"), ( lame_get_force_ms( gfp ) )?wxT("enabled"):wxT("disabled") );
1544 wxPrintf(wxT("Padding Type =%d\n"), (int) lame_get_padding_type( gfp ) );
1545 wxPrintf(wxT("Disable Reservoir =%d\n"), lame_get_disable_reservoir( gfp ) );
1546 wxPrintf(wxT("Allow diff-short =%d\n"), lame_get_allow_diff_short( gfp ) );
1547 wxPrintf(wxT("Interchannel masking =%d\n"), lame_get_interChRatio( gfp ) ); // supposed to be a float, but in lib-src/lame/lame/lame.h it's int
1548 wxPrintf(wxT("Strict ISO Encoding =%s\n"), ( lame_get_strict_ISO( gfp ) ) ?wxT("Yes"):wxT("No"));
1549 wxPrintf(wxT("Scale =%5.2f\n"), lame_get_scale( gfp ) );
1550
1551 wxPrintf(wxT("VBR =%s, VBR_q =%d, VBR method ="),
1552 ( lame_get_VBR( gfp ) !=vbr_off ) ? wxT("enabled"): wxT("disabled"),
1553 lame_get_VBR_q( gfp ) );
1554
1555 switch ( lame_get_VBR( gfp ) )
1556 {
1557 case vbr_off: wxPrintf(wxT( "vbr_off\n" )); break;
1558 case vbr_mt : wxPrintf(wxT( "vbr_mt \n" )); break;
1559 case vbr_rh : wxPrintf(wxT( "vbr_rh \n" )); break;
1560 case vbr_mtrh: wxPrintf(wxT( "vbr_mtrh \n" )); break;
1561 case vbr_abr:
1562 wxPrintf(wxT( "vbr_abr (average bitrate %d kbps)\n"), lame_get_VBR_mean_bitrate_kbps( gfp ) );
1563 break;
1564 default:
1565 wxPrintf(wxT("error, unknown VBR setting\n"));
1566 break;
1567 }
1568
1569 wxPrintf(wxT("Vbr Min bitrate =%d kbps\n"), lame_get_VBR_min_bitrate_kbps( gfp ) );
1570 wxPrintf(wxT("Vbr Max bitrate =%d kbps\n"), lame_get_VBR_max_bitrate_kbps( gfp ) );
1571
1572 wxPrintf(wxT("Write VBR Header =%s\n"), ( lame_get_bWriteVbrTag( gfp ) ) ?wxT("Yes"):wxT("No"));
1573 wxPrintf(wxT("VBR Hard min =%d\n"), lame_get_VBR_hard_min( gfp ) );
1574
1575 wxPrintf(wxT("ATH Only =%d\n"), lame_get_ATHonly( gfp ) );
1576 wxPrintf(wxT("ATH short =%d\n"), lame_get_ATHshort( gfp ) );
1577 wxPrintf(wxT("ATH no =%d\n"), lame_get_noATH( gfp ) );
1578 wxPrintf(wxT("ATH type =%d\n"), lame_get_ATHtype( gfp ) );
1579 wxPrintf(wxT("ATH lower =%f\n"), lame_get_ATHlower( gfp ) );
1580 wxPrintf(wxT("ATH aa =%d\n"), lame_get_athaa_type( gfp ) );
1581 wxPrintf(wxT("ATH aa loudapprox =%d\n"), lame_get_athaa_loudapprox( gfp ) );
1582 wxPrintf(wxT("ATH aa sensitivity =%f\n"), lame_get_athaa_sensitivity( gfp ) );
1583
1584 wxPrintf(wxT("Experimental nspsytune =%d\n"), lame_get_exp_nspsytune( gfp ) );
1585 wxPrintf(wxT("Experimental X =%d\n"), lame_get_experimentalX( gfp ) );
1586 wxPrintf(wxT("Experimental Y =%d\n"), lame_get_experimentalY( gfp ) );
1587 wxPrintf(wxT("Experimental Z =%d\n"), lame_get_experimentalZ( gfp ) );
1588}
1589#endif
1590
1592{
1593 struct
1594 {
1596 unsigned channels;
1597 double t0;
1598 double t1;
1600 wxFFile outFile;
1602 unsigned long id3len;
1603 wxFileOffset infoTagPos;
1606 std::unique_ptr<Mixer> mixer;
1608
1609public:
1611 const Parameters& parameters,
1612 const wxFileNameWrapper& filename,
1613 double t0, double t1, bool selectedOnly,
1614 double sampleRate, unsigned channels,
1615 MixerOptions::Downmix* mixerSpec,
1616 const Tags* tags) override;
1617
1618 ExportResult Process(ExportProcessorDelegate& delegate) override;
1619
1620private:
1621
1622 static int AskResample(int bitrate, int rate, int lowrate, int highrate);
1623 static unsigned long AddTags(ArrayOf<char> &buffer, bool *endOfFile, const Tags *tags);
1624#ifdef USE_LIBID3TAG
1625 static void AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name);
1626#endif
1627};
1628
1629//----------------------------------------------------------------------------
1630// ExportMP3
1631//----------------------------------------------------------------------------
1632
1633class ExportMP3 final : public ExportPlugin
1634{
1635public:
1636
1638 bool CheckFileName(wxFileName & filename, int format) const override;
1639
1640 int GetFormatCount() const override;
1641 FormatInfo GetFormatInfo(int) const override;
1642
1643 std::unique_ptr<ExportOptionsEditor>
1644 CreateOptionsEditor(int, ExportOptionsEditor::Listener* listener) const override;
1645
1646 std::unique_ptr<ExportProcessor> CreateProcessor(int format) const override;
1647
1648 std::vector<std::string> GetMimeTypes(int) const override;
1649
1650 bool ParseConfig(
1651 int formatIndex, const rapidjson::Value& document,
1652 ExportProcessor::Parameters& parameters) const override;
1653};
1654
1655ExportMP3::ExportMP3() = default;
1656
1658{
1659 return 1;
1660}
1661
1663{
1664 return {
1665 wxT("MP3"), XO("MP3 Files"), { wxT("mp3") }, 2u, true
1666 };
1667}
1668
1669std::unique_ptr<ExportOptionsEditor>
1671{
1672 return std::make_unique<MP3ExportOptionsEditor>(listener);
1673}
1674
1675std::unique_ptr<ExportProcessor> ExportMP3::CreateProcessor(int format) const
1676{
1677 return std::make_unique<MP3ExportProcessor>();
1678}
1679
1680std::vector<std::string> ExportMP3::GetMimeTypes(int) const
1681{
1682 return { "audio/mpeg" };
1683}
1684
1686 int formatIndex, const rapidjson::Value& document,
1687 ExportProcessor::Parameters& parameters) const
1688{
1689 if (!document.IsObject())
1690 return false;
1691
1692 MP3OptionID qualityMode;
1693
1694 if (document.HasMember("mode"))
1695 {
1696 auto& mode = document["mode"];
1697 if (!mode.IsString())
1698 return false;
1699
1700 auto value = mode.GetString();
1701
1702 if (value == std::string_view { "SET" })
1703 qualityMode = MP3OptionIDQualitySET;
1704 else if (value == std::string_view { "VBR" })
1705 qualityMode = MP3OptionIDQualityVBR;
1706 else if (value == std::string_view { "ABR" })
1707 qualityMode = MP3OptionIDQualityABR;
1708 else if (value == std::string_view { "CBR" })
1709 qualityMode = MP3OptionIDQualityCBR;
1710 else
1711 return false;
1712
1713 parameters.push_back(std::make_tuple(MP3OptionIDMode, value));
1714 }
1715 else
1716 return false;
1717
1718 if (document.HasMember("quality"))
1719 {
1720 auto& qualityMember = document["quality"];
1721
1722 if (!qualityMember.IsInt())
1723 return false;
1724
1725 const auto quality = qualityMember.GetInt();
1726
1727 if (qualityMode == MP3OptionIDQualitySET && (quality < 0 || quality > 3))
1728 return false;
1729 else if (
1730 qualityMode == MP3OptionIDQualityVBR && (quality < 0 || quality > 9))
1731 return false;
1732 else if (
1733 qualityMode == MP3OptionIDQualityABR &&
1734 std::find(
1735 fixRateValues.begin(), fixRateValues.end(),
1736 ExportValue { quality }) ==
1737 fixRateValues.end())
1738 return false;
1739 else if (
1740 qualityMode == MP3OptionIDQualityCBR &&
1741 std::find(
1742 fixRateValues.begin(), fixRateValues.end(),
1743 ExportValue { quality }) ==
1744 fixRateValues.end())
1745 return false;
1746
1747 parameters.push_back(std::make_tuple(qualityMode, quality));
1748 }
1749 else
1750 return false;
1751
1752 return true;
1753}
1754
1755bool ExportMP3::CheckFileName(wxFileName & WXUNUSED(filename), int WXUNUSED(format)) const
1756{
1757#ifndef DISABLE_DYNAMIC_LOADING_LAME
1758 MP3Exporter exporter;
1759
1760 if (!exporter.LoadLibrary(wxTheApp->GetTopWindow(), MP3Exporter::Maybe)) {
1761 BasicUI::ShowMessageBox(XO("Could not open MP3 encoding library!"),
1763 .IconStyle(BasicUI::Icon::Error)
1764 .Caption(XO("Error")));
1765 gPrefs->Write(wxT("/MP3/MP3LibPath"), wxString(wxT("")));
1766 gPrefs->Flush();
1767
1768 return false;
1769 }
1770#endif // DISABLE_DYNAMIC_LOADING_LAME
1771
1772 return true;
1773}
1774
1776 const Parameters& parameters,
1777 const wxFileNameWrapper& fName,
1778 double t0, double t1, bool selectionOnly,
1779 double sampleRate, unsigned channels,
1780 MixerOptions::Downmix* mixerSpec,
1781 const Tags* metadata)
1782{
1783 context.t0 = t0;
1784 context.t1 = t1;
1785 context.channels = channels;
1786
1787 int rate = lrint(sampleRate);
1788 auto& exporter = context.exporter;
1789
1790#ifdef DISABLE_DYNAMIC_LOADING_LAME
1791 if (!exporter.InitLibrary(wxT(""))) {
1792 gPrefs->Write(wxT("/MP3/MP3LibPath"), wxString(wxT("")));
1793 gPrefs->Flush();
1794 throw ExportException(_("Could not initialize MP3 encoding library!"));
1795 }
1796#else
1797 if (!exporter.LoadLibrary(nullptr, MP3Exporter::Maybe)) {
1798 gPrefs->Write(wxT("/MP3/MP3LibPath"), wxString(wxT("")));
1799 gPrefs->Flush();
1800 throw ExportException(_("Could not open MP3 encoding library!"));
1801 }
1802
1804 gPrefs->Write(wxT("/MP3/MP3LibPath"), wxString(wxT("")));
1805 gPrefs->Flush();
1806 throw ExportException(_("Not a valid or supported MP3 encoding library!"));
1807 }
1808#endif // DISABLE_DYNAMIC_LOADING_LAME
1809
1810 // Retrieve preferences
1811 int highrate = 48000;
1812 int lowrate = 8000;
1813 int bitrate = 0;
1814 int quality;
1815
1817 parameters,
1819 std::string("CBR"));
1820 // Set the bitrate/quality and mode
1821 if (rmode == "SET") {
1822 quality = ExportPluginHelpers::GetParameterValue<int>(
1823 parameters,
1827 exporter.SetQuality(quality);
1828 }
1829 else if (rmode == "VBR") {
1830 quality = ExportPluginHelpers::GetParameterValue<int>(
1831 parameters,
1833 QUALITY_2);
1835 exporter.SetQuality(quality);
1836 }
1837 else if (rmode == "ABR") {
1839 parameters,
1841 128);
1843 exporter.SetBitrate(bitrate);
1844 if (bitrate > 160) {
1845 lowrate = 32000;
1846 }
1847 else if (bitrate < 32 || bitrate == 144) {
1848 highrate = 24000;
1849 }
1850 }
1851 else {
1854 exporter.SetBitrate(bitrate);
1855
1856 if (bitrate > 160) {
1857 lowrate = 32000;
1858 }
1859 else if (bitrate < 32 || bitrate == 144) {
1860 highrate = 24000;
1861 }
1862 }
1863
1864 // Verify sample rate
1865 if (!make_iterator_range( sampRates ).contains( rate ) ||
1866 (rate < lowrate) || (rate > highrate)) {
1867 // Force valid sample rate in macros.
1868 if (project.mBatchMode) {
1869 if (!make_iterator_range( sampRates ).contains( rate )) {
1870 auto const bestRateIt = std::lower_bound(sampRates.begin(),
1871 sampRates.end(), rate);
1872 rate = (bestRateIt == sampRates.end()) ? highrate : *bestRateIt;
1873 }
1874 if (rate < lowrate) {
1875 rate = lowrate;
1876 }
1877 else if (rate > highrate) {
1878 rate = highrate;
1879 }
1880 }
1881 // else validate or prompt
1882 else {
1883 if (!make_iterator_range( sampRates ).contains( rate ) ||
1884 (rate < lowrate) || (rate > highrate)) {
1885 //This call should go away once export project rate option
1886 //is available as an export dialog option
1887 rate = AskResample(bitrate, rate, lowrate, highrate);
1888 }
1889 if (rate == 0) {
1890 return false;
1891 }
1892 }
1893 }
1894
1895 context.inSamples = exporter.InitializeStream(channels, rate);
1896 if (context.inSamples < 0) {
1897 throw ExportException(_("Unable to initialize MP3 stream"));
1898 }
1899
1900 // Put ID3 tags at beginning of file
1901 if (metadata == nullptr)
1902 metadata = &Tags::Get( project );
1903
1904 // Open file for writing
1905 if (!context.outFile.Open(fName.GetFullPath(), wxT("w+b"))) {
1906 throw ExportException(_("Unable to open target file for writing"));
1907 }
1908
1909 bool endOfFile;
1910 context.id3len = AddTags(context.id3buffer, &endOfFile, metadata);
1911 if (context.id3len && !endOfFile) {
1912 if (context.id3len > context.outFile.Write(context.id3buffer.get(), context.id3len)) {
1913 // TODO: more precise message
1914 throw ExportErrorException("MP3:1882");
1915 }
1916 context.id3len = 0;
1917 context.id3buffer.reset();
1918 }
1919
1920 context.infoTagPos = context.outFile.Tell();
1921
1922 context.bufferSize = std::max(0, exporter.GetOutBufferSize());
1923 if (context.bufferSize == 0) {
1924 // TODO: more precise message
1925 throw ExportErrorException("MP3:1849");
1926 }
1927
1928 if (rmode == "SET") {
1929 context.status = (selectionOnly ?
1930 XO("Exporting selected audio with %s preset") :
1931 XO("Exporting the audio with %s preset"))
1932 .Format( setRateNamesShort[quality] );
1933 }
1934 else if (rmode == "VBR") {
1935 context.status = (selectionOnly ?
1936 XO("Exporting selected audio with VBR quality %s") :
1937 XO("Exporting the audio with VBR quality %s"))
1938 .Format( varRateNames[quality] );
1939 }
1940 else {
1941 context.status = (selectionOnly ?
1942 XO("Exporting selected audio at %d Kbps") :
1943 XO("Exporting the audio at %d Kbps"))
1944 .Format( bitrate );
1945 }
1946
1948 project, selectionOnly, t0, t1, channels, context.inSamples, true, rate,
1949 floatSample, mixerSpec);
1950
1951 return true;
1952}
1953
1955{
1956 delegate.SetStatusString(context.status);
1957
1958 auto& exporter = context.exporter;
1959 int bytes = 0;
1960
1961 ArrayOf<unsigned char> buffer{ context.bufferSize };
1962 wxASSERT(buffer);
1963
1964 auto exportResult = ExportResult::Success;
1965
1966 {
1967 while (exportResult == ExportResult::Success) {
1968 auto blockLen = context.mixer->Process();
1969 if (blockLen == 0)
1970 break;
1971
1972 float *mixed = (float *)context.mixer->GetBuffer();
1973
1974 if ((int)blockLen < context.inSamples) {
1975 if (context.channels > 1) {
1976 bytes = exporter.EncodeRemainder(mixed, blockLen, buffer.get());
1977 }
1978 else {
1979 bytes = exporter.EncodeRemainderMono(mixed, blockLen, buffer.get());
1980 }
1981 }
1982 else {
1983 if (context.channels > 1) {
1984 bytes = exporter.EncodeBuffer(mixed, buffer.get());
1985 }
1986 else {
1987 bytes = exporter.EncodeBufferMono(mixed, buffer.get());
1988 }
1989 }
1990
1991 if (bytes < 0) {
1992 throw ExportException(XO("Error %ld returned from MP3 encoder")
1993 .Format( bytes )
1994 .Translation());
1995 }
1996
1997 if (bytes > (int)context.outFile.Write(buffer.get(), bytes)) {
1998 // TODO: more precise message
1999 throw ExportDiskFullError(context.outFile.GetName());
2000 }
2001
2002 if(exportResult == ExportResult::Success)
2004 delegate, *context.mixer, context.t0, context.t1);
2005 }
2006 }
2007
2008 if (exportResult == ExportResult::Success) {
2009 bytes = exporter.FinishStream(buffer.get());
2010
2011 if (bytes < 0) {
2012 // TODO: more precise message
2013 throw ExportErrorException("MP3:1981");
2014 }
2015
2016 if (bytes > 0) {
2017 if (bytes > (int)context.outFile.Write(buffer.get(), bytes)) {
2018 // TODO: more precise message
2019 throw ExportErrorException("MP3:1988");
2020 }
2021 }
2022
2023 // Write ID3 tag if it was supposed to be at the end of the file
2024 if (context.id3len > 0) {
2025 if (bytes > (int)context.outFile.Write(context.id3buffer.get(), context.id3len)) {
2026 // TODO: more precise message
2027 throw ExportErrorException("MP3:1997");
2028 }
2029 }
2030
2031 // Always write the info (Xing/Lame) tag. Until we stop supporting Lame
2032 // versions before 3.98, we must do this after the MP3 file has been
2033 // closed.
2034 //
2035 // Also, if beWriteInfoTag() is used, mGF will no longer be valid after
2036 // this call, so do not use it.
2037 if (!exporter.PutInfoTag(context.outFile, context.infoTagPos) ||
2038 !context.outFile.Flush() ||
2039 !context.outFile.Close()) {
2040 // TODO: more precise message
2041 throw ExportErrorException("MP3:2012");
2042 }
2043 }
2044 return exportResult;
2045}
2046
2047int MP3ExportProcessor::AskResample(int bitrate, int rate, int lowrate, int highrate)
2048{
2049 wxDialogWrapper d(nullptr, wxID_ANY, XO("Invalid sample rate"));
2050 d.SetName();
2051 wxChoice *choice;
2053
2054 int selected = -1;
2055
2056 S.StartVerticalLay();
2057 {
2058 S.SetBorder(10);
2059 S.StartStatic(XO("Resample"));
2060 {
2061 S.StartHorizontalLay(wxALIGN_CENTER, false);
2062 {
2063 S.AddTitle(
2064 ((bitrate == 0)
2065 ? XO(
2066"The project sample rate (%d) is not supported by the MP3\nfile format. ")
2067 .Format( rate )
2068 : XO(
2069"The project sample rate (%d) and bit rate (%d kbps) combination is not\nsupported by the MP3 file format. ")
2070 .Format( rate, bitrate ))
2071 + XO("You may resample to one of the rates below.")
2072 );
2073 }
2074 S.EndHorizontalLay();
2075
2076 S.StartHorizontalLay(wxALIGN_CENTER, false);
2077 {
2078 choice = S.AddChoice(XXO("Sample Rates"),
2079 [&]{
2080 TranslatableStrings choices;
2081 for (size_t ii = 0, nn = sampRates.size(); ii < nn; ++ii) {
2082 int label = sampRates[ii];
2083 if (label >= lowrate && label <= highrate) {
2084 choices.push_back( Verbatim( "%d" ).Format( label ) );
2085 if (label <= rate)
2086 selected = ii;
2087 }
2088 }
2089 return choices;
2090 }(),
2091 std::max( 0, selected )
2092 );
2093 }
2094 S.EndHorizontalLay();
2095 }
2096 S.EndStatic();
2097
2098 S.AddStandardButtons();
2099 }
2100 S.EndVerticalLay();
2101
2102 d.Layout();
2103 d.Fit();
2104 d.SetMinSize(d.GetSize());
2105 d.Center();
2106
2107 if (d.ShowModal() == wxID_CANCEL) {
2108 return 0;
2109 }
2110
2111 return wxAtoi(choice->GetStringSelection());
2112}
2113
2114#ifdef USE_LIBID3TAG
2115struct id3_tag_deleter {
2116 void operator () (id3_tag *p) const { if (p) id3_tag_delete(p); }
2117};
2118using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
2119#endif
2120
2121// returns buffer len; caller frees
2122unsigned long MP3ExportProcessor::AddTags(ArrayOf<char> &buffer, bool *endOfFile, const Tags *tags)
2123{
2124#ifdef USE_LIBID3TAG
2125 id3_tag_holder tp { id3_tag_new() };
2126
2127 for (const auto &pair : tags->GetRange()) {
2128 const auto &n = pair.first;
2129 const auto &v = pair.second;
2130 const char *name = "TXXX";
2131
2132 if (n.CmpNoCase(TAG_TITLE) == 0) {
2133 name = ID3_FRAME_TITLE;
2134 }
2135 else if (n.CmpNoCase(TAG_ARTIST) == 0) {
2136 name = ID3_FRAME_ARTIST;
2137 }
2138 else if (n.CmpNoCase(TAG_ALBUM) == 0) {
2139 name = ID3_FRAME_ALBUM;
2140 }
2141 else if (n.CmpNoCase(TAG_YEAR) == 0) {
2142 // LLL: Some apps do not like the newer frame ID (ID3_FRAME_YEAR),
2143 // so we add old one as well.
2144 AddFrame(tp.get(), n, v, "TYER");
2145 name = ID3_FRAME_YEAR;
2146 }
2147 else if (n.CmpNoCase(TAG_GENRE) == 0) {
2148 name = ID3_FRAME_GENRE;
2149 }
2150 else if (n.CmpNoCase(TAG_COMMENTS) == 0) {
2151 name = ID3_FRAME_COMMENT;
2152 }
2153 else if (n.CmpNoCase(TAG_TRACK) == 0) {
2154 name = ID3_FRAME_TRACK;
2155 }
2156
2157 AddFrame(tp.get(), n, v, name);
2158 }
2159
2160 tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression
2161
2162 // If this version of libid3tag supports it, use v2.3 ID3
2163 // tags instead of the newer, but less well supported, v2.4
2164 // that libid3tag uses by default.
2165 #ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
2166 tp->options |= ID3_TAG_OPTION_ID3V2_3;
2167 #endif
2168
2169 *endOfFile = false;
2170
2171 unsigned long len;
2172
2173 len = id3_tag_render(tp.get(), 0);
2174 buffer.reinit(len);
2175 len = id3_tag_render(tp.get(), (id3_byte_t *)buffer.get());
2176
2177 return len;
2178#else //ifdef USE_LIBID3TAG
2179 return 0;
2180#endif
2181}
2182
2183#ifdef USE_LIBID3TAG
2184void MP3ExportProcessor::AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name)
2185{
2186 struct id3_frame *frame = id3_frame_new(name);
2187
2188 if (!n.IsAscii() || !v.IsAscii()) {
2189 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
2190 }
2191 else {
2192 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
2193 }
2194
2196 id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) v.mb_str(wxConvUTF8)) };
2197
2198 if (strcmp(name, ID3_FRAME_COMMENT) == 0) {
2199 // A hack to get around iTunes not recognizing the comment. The
2200 // language defaults to XXX and, since it's not a valid language,
2201 // iTunes just ignores the tag. So, either set it to a valid language
2202 // (which one???) or just clear it. Unfortunately, there's no supported
2203 // way of clearing the field, so do it directly.
2204 struct id3_frame *frame2 = id3_frame_new(name);
2205 id3_field_setfullstring(id3_frame_field(frame2, 3), ucs4.get());
2206 id3_field *f2 = id3_frame_field(frame2, 1);
2207 memset(f2->immediate.value, 0, sizeof(f2->immediate.value));
2208 id3_tag_attachframe(tp, frame2);
2209 // Now install a second frame with the standard default language = "XXX"
2210 id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
2211 }
2212 else if (strcmp(name, "TXXX") == 0) {
2213 id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
2214
2215 ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) n.mb_str(wxConvUTF8)));
2216
2217 id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
2218 }
2219 else {
2220 auto addr = ucs4.get();
2221 id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
2222 }
2223
2224 id3_tag_attachframe(tp, frame);
2225}
2226#endif
2227
2229 []{ return std::make_unique< ExportMP3 >(); }
2230};
2231
2232//----------------------------------------------------------------------------
2233// Return library version
2234//----------------------------------------------------------------------------
2235
2236TranslatableString GetMP3Version(wxWindow *parent, bool prompt)
2237{
2238 MP3Exporter exporter;
2239 auto versionString = XO("MP3 export library not found");
2240
2241#ifndef DISABLE_DYNAMIC_LOADING_LAME
2242 if (prompt) {
2243 exporter.FindLibrary(parent);
2244 }
2245
2246 if (exporter.LoadLibrary(parent, prompt ? MP3Exporter::Yes : MP3Exporter::No)) {
2247#endif // DISABLE_DYNAMIC_LOADING_LAME
2248 versionString = Verbatim( exporter.GetLibraryVersion() );
2249#ifdef MP3_EXPORT_BUILT_IN
2250 versionString.Join( XO("(Built-in)"), " " );
2251#endif
2252
2253#ifndef DISABLE_DYNAMIC_LOADING_LAME
2254 }
2255#endif // DISABLE_DYNAMIC_LOADING_LAME
2256
2257 return versionString;
2258}
2259
wxT("CloseDown"))
Toolkit-neutral facade for basic user interface services.
END_EVENT_TABLE()
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:76
const wxChar * values
int lame_set_VBR_t(lame_global_flags *, vbr_mode)
Definition: ExportMP3.cpp:640
static const std::vector< ExportValue > fixRateValues
Definition: ExportMP3.cpp:144
int lame_set_bWriteVbrTag_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:647
static const std::vector< int > sampRates
Definition: ExportMP3.cpp:199
int CDECL lame_encode_buffer_interleaved_ieee_float_t(lame_t gfp, const float pcm[], const int nsamples, unsigned char *mp3buf, const int mp3buf_size)
Definition: ExportMP3.cpp:621
static const TranslatableStrings varRateNames
Definition: ExportMP3.cpp:165
#define ID_DLOAD
Definition: ExportMP3.cpp:474
static const TranslatableStrings fixRateNames
Definition: ExportMP3.cpp:123
const char * get_lame_version_t(void)
Definition: ExportMP3.cpp:611
int lame_set_VBR_q_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:641
int lame_set_in_samplerate_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:635
int lame_close_t(lame_global_flags *)
Definition: ExportMP3.cpp:633
TranslatableString n_kbps(int n)
Definition: ExportMP3.cpp:121
TranslatableString GetMP3Version(wxWindow *parent, bool prompt)
Definition: ExportMP3.cpp:2236
MP3OptionID
Definition: ExportMP3.cpp:211
@ MP3OptionIDQualityABR
Definition: ExportMP3.cpp:215
@ MP3OptionIDQualityVBR
Definition: ExportMP3.cpp:214
@ MP3OptionIDQualityCBR
Definition: ExportMP3.cpp:216
@ MP3OptionIDQualitySET
Definition: ExportMP3.cpp:213
@ MP3OptionIDMode
Definition: ExportMP3.cpp:212
void lame_mp3_tags_fid_t(lame_global_flags *, FILE *)
Definition: ExportMP3.cpp:649
int lame_set_VBR_min_bitrate_kbps_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:642
int CDECL lame_encode_buffer_ieee_float_t(lame_t gfp, const float pcm_l[], const float pcm_r[], const int nsamples, unsigned char *mp3buf, const int mp3buf_size)
Definition: ExportMP3.cpp:613
static ExportPluginRegistry::RegisteredPlugin sRegisteredPlugin
Definition: ExportMP3.cpp:2228
int lame_set_error_protection_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:645
unsigned long beWriteInfoTag_t(lame_global_flags *, char *)
Definition: ExportMP3.cpp:657
static const TranslatableStrings setRateNames
Definition: ExportMP3.cpp:183
int lame_set_out_samplerate_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:636
int lame_set_quality_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:638
const std::initializer_list< ExportOption > MP3Options
Definition: ExportMP3.cpp:220
void beVersion_t(be_version *)
Definition: ExportMP3.cpp:689
int lame_set_preset_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:644
#define ID_BROWSE
Definition: ExportMP3.cpp:473
size_t lame_get_lametag_frame_t(const lame_global_flags *, unsigned char *buffer, size_t size)
Definition: ExportMP3.cpp:648
static const TranslatableStrings setRateNamesShort
Definition: ExportMP3.cpp:191
int lame_set_disable_reservoir_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:646
@ QUALITY_2
Definition: ExportMP3.cpp:109
@ PRESET_EXTREME
Definition: ExportMP3.cpp:115
@ PRESET_INSANE
Definition: ExportMP3.cpp:114
@ PRESET_STANDARD
Definition: ExportMP3.cpp:116
@ PRESET_MEDIUM
Definition: ExportMP3.cpp:117
lame_global_flags * lame_init_t(void)
Definition: ExportMP3.cpp:609
int lame_encode_flush_t(lame_global_flags *gf, unsigned char *mp3buf, int size)
Definition: ExportMP3.cpp:628
int lame_set_num_channels_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:637
int lame_set_mode_t(lame_global_flags *, MPEG_mode)
Definition: ExportMP3.cpp:643
int lame_set_brate_t(lame_global_flags *, int)
Definition: ExportMP3.cpp:639
int lame_init_params_t(lame_global_flags *)
Definition: ExportMP3.cpp:610
@ MODE_SET
Definition: ExportMP3.h:19
@ MODE_ABR
Definition: ExportMP3.h:21
@ MODE_VBR
Definition: ExportMP3.h:20
@ MODE_CBR
Definition: ExportMP3.h:22
std::variant< bool, int, double, std::string > ExportValue
A type of option values (parameters) used by exporting plugins.
Definition: ExportTypes.h:38
ExportResult
Definition: ExportTypes.h:24
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
IteratorRange< Iterator > make_iterator_range(const Iterator &i1, const Iterator &i2)
Definition: IteratorX.h:210
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:148
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
EffectReverbSettings preset
Definition: Reverb.cpp:44
FilePath SelectFile(FileNames::Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
Definition: SelectFile.cpp:17
#define OSOUTPUT(X)
Definition: SelectFile.h:48
@ eIsCreating
Definition: ShuttleGui.h:37
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
const auto project
#define S(N)
Definition: ToChars.cpp:64
declares abstract base class Track, TrackList, and iterators over TrackList
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
static const auto fn
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:59
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
bool CheckFileName(wxFileName &filename, int format) const override
Definition: ExportMP3.cpp:1755
int GetFormatCount() const override
Definition: ExportMP3.cpp:1657
std::vector< std::string > GetMimeTypes(int) const override
Definition: ExportMP3.cpp:1680
bool ParseConfig(int formatIndex, const rapidjson::Value &document, ExportProcessor::Parameters &parameters) const override
Attempt to parse configuration JSON object and produce a suitable set of parameters....
Definition: ExportMP3.cpp:1685
std::unique_ptr< ExportProcessor > CreateProcessor(int format) const override
Definition: ExportMP3.cpp:1675
std::unique_ptr< ExportOptionsEditor > CreateOptionsEditor(int, ExportOptionsEditor::Listener *listener) const override
Creates format-dependent options editor, that is used to create a valid set of parameters to be used ...
Definition: ExportMP3.cpp:1670
FormatInfo GetFormatInfo(int) const override
Returns FormatInfo structure for given index if it's valid, or a default one. FormatInfo::format isn'...
Definition: ExportMP3.cpp:1662
Listener object that is used to report on option changes.
virtual void OnExportOptionChangeEnd()=0
Called after OnExportOptionChange
virtual void OnExportOptionChangeBegin()=0
Called before OnExportOptionChange
virtual void OnExportOptionChange(const ExportOption &option)=0
Called when option change.
virtual void OnSampleRateListChange()=0
Editor objects are used to retrieve a set of export options, and configure exporting parameters accor...
std::vector< int > SampleRateList
static T GetParameterValue(const ExportProcessor::Parameters &parameters, int id, T defaultValue=T())
static ExportResult UpdateProgress(ExportProcessorDelegate &delegate, Mixer &mixer, double t0, double t1)
Sends progress update to delegate and retrieves state update from it. Typically used inside each expo...
static std::unique_ptr< Mixer > CreateMixer(const AudacityProject &project, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerOptions::Downmix *mixerSpec)
virtual void SetStatusString(const TranslatableString &str)=0
std::vector< std::tuple< ExportOptionID, ExportValue > > Parameters
Definition: ExportPlugin.h:93
std::vector< FileType > FileTypes
Definition: FileNames.h:75
FILES_API const FileType AllFiles
Definition: FileNames.h:70
FILES_API const FileType DynamicLibraries
Definition: FileNames.h:72
wxTextCtrl * mPathText
Definition: ExportMP3.cpp:591
void OnBrowse(wxCommandEvent &WXUNUSED(event))
Definition: ExportMP3.cpp:548
wxFileName mLibPath
Definition: ExportMP3.cpp:584
wxString GetLibPath()
Definition: ExportMP3.cpp:574
FindDialog(wxWindow *parent, wxString path, wxString name, FileNames::FileTypes types)
Definition: ExportMP3.cpp:482
FileNames::FileTypes mTypes
Definition: ExportMP3.cpp:588
wxString mPath
Definition: ExportMP3.cpp:586
void PopulateOrExchange(ShuttleGui &S)
Definition: ExportMP3.cpp:500
void OnDownload(wxCommandEvent &WXUNUSED(event))
Definition: ExportMP3.cpp:569
wxString mName
Definition: ExportMP3.cpp:587
Abstract base class used in importing a file.
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:231
int GetOptionsCount() const override
Definition: ExportMP3.cpp:286
bool GetOption(int index, ExportOption &option) const override
Definition: ExportMP3.cpp:291
std::unordered_map< int, ExportValue > mValues
Definition: ExportMP3.cpp:273
void OnModeChange(const std::string &mode)
Definition: ExportMP3.cpp:428
bool SetValue(int id, const ExportValue &value) override
Definition: ExportMP3.cpp:301
SampleRateList GetSampleRateList() const override
Definition: ExportMP3.cpp:353
bool GetValue(int id, ExportValue &value) const override
Definition: ExportMP3.cpp:342
MP3ExportOptionsEditor(Listener *listener)
Definition: ExportMP3.cpp:277
void Load(const audacity::BasicSettings &config) override
Definition: ExportMP3.cpp:390
std::vector< ExportOption > mOptions
Definition: ExportMP3.cpp:272
void Store(audacity::BasicSettings &config) const override
Definition: ExportMP3.cpp:411
MP3Exporter exporter
Definition: ExportMP3.cpp:1599
static int AskResample(int bitrate, int rate, int lowrate, int highrate)
Definition: ExportMP3.cpp:2047
unsigned long id3len
Definition: ExportMP3.cpp:1602
bool Initialize(AudacityProject &project, const Parameters &parameters, const wxFileNameWrapper &filename, double t0, double t1, bool selectedOnly, double sampleRate, unsigned channels, MixerOptions::Downmix *mixerSpec, const Tags *tags) override
Called before start processing.
Definition: ExportMP3.cpp:1775
static unsigned long AddTags(ArrayOf< char > &buffer, bool *endOfFile, const Tags *tags)
Definition: ExportMP3.cpp:2122
ExportResult Process(ExportProcessorDelegate &delegate) override
Definition: ExportMP3.cpp:1954
wxFileOffset infoTagPos
Definition: ExportMP3.cpp:1603
struct MP3ExportProcessor::@174 context
ArrayOf< char > id3buffer
Definition: ExportMP3.cpp:1601
std::unique_ptr< Mixer > mixer
Definition: ExportMP3.cpp:1606
TranslatableString status
Definition: ExportMP3.cpp:1595
Class used to export MP3 files.
Definition: ExportMP3.cpp:693
lame_set_VBR_q_t * lame_set_VBR_q
Definition: ExportMP3.cpp:785
static const int mOutBufferSize
Definition: ExportMP3.cpp:805
lame_set_in_samplerate_t * lame_set_in_samplerate
Definition: ExportMP3.cpp:779
wxString GetLibraryVersion()
Definition: ExportMP3.cpp:1159
bool InitLibraryExternal(wxString libpath)
Definition: ExportMP3.cpp:1028
int EncodeBufferMono(float inbuffer[], unsigned char outbuffer[])
Definition: ExportMP3.cpp:1297
lame_set_disable_reservoir_t * lame_set_disable_reservoir
Definition: ExportMP3.cpp:790
int EncodeRemainderMono(float inbuffer[], int nSamples, unsigned char outbuffer[])
Definition: ExportMP3.cpp:1307
bool mLibIsExternal
Definition: ExportMP3.cpp:751
int GetOutBufferSize()
Definition: ExportMP3.cpp:1268
bool PutInfoTag(wxFFile &f, wxFileOffset off)
Definition: ExportMP3.cpp:1344
lame_mp3_tags_fid_t * lame_mp3_tags_fid
Definition: ExportMP3.cpp:793
unsigned char mInfoTagBuf[2880]
Definition: ExportMP3.cpp:809
void SetBitrate(int rate)
Definition: ExportMP3.cpp:961
size_t mInfoTagLen
Definition: ExportMP3.cpp:810
lame_init_t * lame_init
Definition: ExportMP3.cpp:771
bool ValidLibraryLoaded()
Definition: ExportMP3.cpp:949
lame_set_brate_t * lame_set_brate
Definition: ExportMP3.cpp:783
lame_encode_buffer_ieee_float_t * lame_encode_buffer_ieee_float
Definition: ExportMP3.cpp:773
lame_encode_buffer_interleaved_ieee_float_t * lame_encode_buffer_interleaved_ieee_float
Definition: ExportMP3.cpp:774
bool mEncoding
Definition: ExportMP3.cpp:763
lame_get_lametag_frame_t * lame_get_lametag_frame
Definition: ExportMP3.cpp:792
lame_set_num_channels_t * lame_set_num_channels
Definition: ExportMP3.cpp:781
void FreeLibrary()
Definition: ExportMP3.cpp:1145
int EncodeBuffer(float inbuffer[], unsigned char outbuffer[])
Definition: ExportMP3.cpp:1276
get_lame_version_t * get_lame_version
Definition: ExportMP3.cpp:777
static const int mSamplesPerChunk
Definition: ExportMP3.cpp:802
bool FindLibrary(wxWindow *parent)
Definition: ExportMP3.cpp:851
void CancelEncoding()
Definition: ExportMP3.cpp:1339
bool LoadLibrary(wxWindow *parent, AskUser askuser)
Definition: ExportMP3.cpp:886
wxString GetLibraryName()
Definition: ExportMP3.cpp:1395
lame_set_VBR_t * lame_set_VBR
Definition: ExportMP3.cpp:784
FileNames::FileTypes GetLibraryTypes()
Definition: ExportMP3.cpp:1400
wxDynamicLibrary lame_lib
Definition: ExportMP3.cpp:755
bool InitLibrary(wxString libpath)
Definition: ExportMP3.cpp:971
lame_global_flags * mGF
Definition: ExportMP3.cpp:800
wxString GetLibraryPath()
Definition: ExportMP3.cpp:1377
lame_close_t * lame_close
Definition: ExportMP3.cpp:776
int FinishStream(unsigned char outbuffer[])
Definition: ExportMP3.cpp:1318
lame_set_quality_t * lame_set_quality
Definition: ExportMP3.cpp:782
wxString mLibPath
Definition: ExportMP3.cpp:754
beWriteInfoTag_t * beWriteInfoTag
Definition: ExportMP3.cpp:795
lame_encode_flush_t * lame_encode_flush
Definition: ExportMP3.cpp:775
void SetMode(int mode)
Definition: ExportMP3.cpp:956
int InitializeStream(unsigned channels, int sampleRate)
Definition: ExportMP3.cpp:1170
lame_set_error_protection_t * lame_set_error_protection
Definition: ExportMP3.cpp:789
lame_set_VBR_min_bitrate_kbps_t * lame_set_VBR_min_bitrate_kbps
Definition: ExportMP3.cpp:786
void SetQuality(int q)
Definition: ExportMP3.cpp:966
bool InitLibraryInternal()
Definition: ExportMP3.cpp:976
int EncodeRemainder(float inbuffer[], int nSamples, unsigned char outbuffer[])
Definition: ExportMP3.cpp:1286
lame_set_out_samplerate_t * lame_set_out_samplerate
Definition: ExportMP3.cpp:780
lame_set_mode_t * lame_set_mode
Definition: ExportMP3.cpp:787
TranslatableString mBladeVersion
Definition: ExportMP3.cpp:760
lame_set_bWriteVbrTag_t * lame_set_bWriteVbrTag
Definition: ExportMP3.cpp:791
lame_init_params_t * lame_init_params
Definition: ExportMP3.cpp:772
bool mLibraryLoaded
Definition: ExportMP3.cpp:756
beVersion_t * beVersion
Definition: ExportMP3.cpp:796
lame_set_preset_t * lame_set_preset
Definition: ExportMP3.cpp:788
A matrix of booleans, one row per input channel, column per output.
Definition: MixerOptions.h:32
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
ID3 Tags (for MP3)
Definition: Tags.h:73
Iterators GetRange() const
Definition: Tags.cpp:426
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
Holds a msgid for the translation catalog; may also bind format arguments.
Base class for objects that provide facility to store data persistently, and access it with string ke...
Definition: BasicSettings.h:31
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0
virtual bool Read(const wxString &key, bool *value) const =0
struct with zillion of control parameters that control lame export (MP3 Conversion DLL).
void SetName(const TranslatableString &title)
#define lrint(dbl)
Definition: float_cast.h:169
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:287
FILES_API FilePath PathFromAddr(void *addr)
BuiltinCommandsModule::Registration< CompareAudioCommand > reg
int ValidateIndex(const std::vector< int > &values, int value, int defaultIndex)
Definition: ExportMP3.cpp:460
int ValidateValue(const std::vector< int > &values, int value, int defaultValue)
Definition: ExportMP3.cpp:453
A type that provides a description of an exporting option. Isn't allowed to change except non-type re...
Definition: ExportTypes.h:43
@ TypeEnum
List/enum option. values holds items, and names text to be displayed.
Definition: ExportTypes.h:48
@ Hidden
Option is not used and may be hidden from the user.
Definition: ExportTypes.h:51
BYTE byMinorVersion
Definition: ExportMP3.cpp:671
BYTE byDLLMinorVersion
Definition: ExportMP3.cpp:666
BYTE byBetaLevel
Definition: ExportMP3.cpp:684
BYTE byMMXEnabled
Definition: ExportMP3.cpp:685
BYTE byMajorVersion
Definition: ExportMP3.cpp:670
BYTE byAlphaLevel
Definition: ExportMP3.cpp:683
BYTE byDLLMajorVersion
Definition: ExportMP3.cpp:665
BYTE byMonth
Definition: ExportMP3.cpp:676