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