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