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 *******************************************************************//**
56 
57 \class MP3Exporter
58 \brief Class used to export MP3 files
59 
60 *//********************************************************************/
61 
62 
63 #include "ExportMP3.h"
64 
65 #include <wx/app.h>
66 #include <wx/defs.h>
67 
68 #include <wx/choice.h>
69 #include <wx/checkbox.h>
70 #include <wx/dynlib.h>
71 #include <wx/ffile.h>
72 #include <wx/intl.h>
73 #include <wx/log.h>
74 #include <wx/mimetype.h>
75 #include <wx/radiobut.h>
76 #include <wx/stattext.h>
77 #include <wx/textctrl.h>
78 #include <wx/timer.h>
79 #include <wx/utils.h>
80 #include <wx/window.h>
81 
82 #include "FileNames.h"
83 #include "float_cast.h"
84 #include "../Mix.h"
85 #include "Prefs.h"
86 #include "ProjectRate.h"
87 #include "../ProjectSettings.h"
88 #include "../ProjectWindow.h"
89 #include "../SelectFile.h"
90 #include "../ShuttleGui.h"
91 #include "../Tags.h"
92 #include "../Track.h"
93 #include "../widgets/HelpSystem.h"
94 #include "../widgets/AudacityMessageBox.h"
95 #include "../widgets/ProgressDialog.h"
96 #include "wxFileNameWrapper.h"
97 
98 #include "Export.h"
99 
100 #include <lame/lame.h>
101 
102 #ifdef USE_LIBID3TAG
103 #include <id3tag.h>
104 #endif
105 
106 //----------------------------------------------------------------------------
107 // ExportMP3Options
108 //----------------------------------------------------------------------------
109 
110 enum MP3ChannelMode : unsigned {
111    CHANNEL_JOINT = 0,
112    CHANNEL_STEREO = 1,
113    CHANNEL_MONO = 2,
114 };
115 
116 enum : int {
117    QUALITY_2 = 2,
118 
119    //ROUTINE_FAST = 0,
120    //ROUTINE_STANDARD = 1,
121 
122    PRESET_INSANE = 0,
123    PRESET_EXTREME = 1,
124    PRESET_STANDARD = 2,
125    PRESET_MEDIUM = 3,
126 };
127 
128 /* i18n-hint: kbps is the bitrate of the MP3 file, kilobits per second*/
n_kbps(int n)129 inline TranslatableString n_kbps( int n ){ return XO("%d kbps").Format( n ); }
130 
131 static const TranslatableStrings fixRateNames {
132    n_kbps(320),
133    n_kbps(256),
134    n_kbps(224),
135    n_kbps(192),
136    n_kbps(160),
137    n_kbps(144),
138    n_kbps(128),
139    n_kbps(112),
140    n_kbps(96),
141    n_kbps(80),
142    n_kbps(64),
143    n_kbps(56),
144    n_kbps(48),
145    n_kbps(40),
146    n_kbps(32),
147    n_kbps(24),
148    n_kbps(16),
149    n_kbps(8),
150 };
151 
152 static const std::vector<int> fixRateValues {
153    320,
154    256,
155    224,
156    192,
157    160,
158    144,
159    128,
160    112,
161    96,
162    80,
163    64,
164    56,
165    48,
166    40,
167    32,
168    24,
169    16,
170    8,
171 };
172 
173 static const TranslatableStrings varRateNames {
174    XO("220-260 kbps (Best Quality)"),
175    XO("200-250 kbps"),
176    XO("170-210 kbps"),
177    XO("155-195 kbps"),
178    XO("145-185 kbps"),
179    XO("110-150 kbps"),
180    XO("95-135 kbps"),
181    XO("80-120 kbps"),
182    XO("65-105 kbps"),
183    XO("45-85 kbps (Smaller files)"),
184 };
185 /*
186 static const TranslatableStrings varModeNames {
187    XO("Fast"),
188    XO("Standard"),
189 };
190 */
191 static const TranslatableStrings setRateNames {
192    /* i18n-hint: Slightly humorous - as in use an insane precision with MP3.*/
193    XO("Insane, 320 kbps"),
194    XO("Extreme, 220-260 kbps"),
195    XO("Standard, 170-210 kbps"),
196    XO("Medium, 145-185 kbps"),
197 };
198 
199 static const TranslatableStrings setRateNamesShort {
200    /* i18n-hint: Slightly humorous - as in use an insane precision with MP3.*/
201    XO("Insane"),
202    XO("Extreme"),
203    XO("Standard"),
204    XO("Medium"),
205 };
206 
207 static const std::vector< int > sampRates {
208    8000,
209    11025,
210    12000,
211    16000,
212    22050,
213    24000,
214    32000,
215    44100,
216    48000,
217 };
218 
219 #define ID_SET 7000
220 #define ID_VBR 7001
221 #define ID_ABR 7002
222 #define ID_CBR 7003
223 #define ID_QUALITY 7004
224 #define ID_MONO 7005
225 
226 class ExportMP3Options final : public wxPanelWrapper
227 {
228 public:
229 
230    ExportMP3Options(wxWindow *parent, int format);
231    virtual ~ExportMP3Options();
232 
233    void PopulateOrExchange(ShuttleGui & S);
234    bool TransferDataToWindow() override;
235    bool TransferDataFromWindow() override;
236 
237    void OnSET(wxCommandEvent& evt);
238    void OnVBR(wxCommandEvent& evt);
239    void OnABR(wxCommandEvent& evt);
240    void OnCBR(wxCommandEvent& evt);
241    void OnQuality(wxCommandEvent& evt);
242    void OnMono(wxCommandEvent& evt);
243 
244    void LoadNames(const TranslatableStrings &choices);
245 
246 private:
247 
248    wxRadioButton *mStereo;
249    wxRadioButton *mJoint;
250    wxCheckBox    *mMono;
251    wxRadioButton *mSET;
252    wxRadioButton *mVBR;
253    wxRadioButton *mABR;
254    wxRadioButton *mCBR;
255    wxChoice *mRate;
256    //wxChoice *mMode;
257 
258    long mSetRate;
259    long mVbrRate;
260    long mAbrRate;
261    long mCbrRate;
262 
263    DECLARE_EVENT_TABLE()
264 };
265 
BEGIN_EVENT_TABLE(ExportMP3Options,wxPanelWrapper)266 BEGIN_EVENT_TABLE(ExportMP3Options, wxPanelWrapper)
267    EVT_RADIOBUTTON(ID_SET,    ExportMP3Options::OnSET)
268    EVT_RADIOBUTTON(ID_VBR,    ExportMP3Options::OnVBR)
269    EVT_RADIOBUTTON(ID_ABR,    ExportMP3Options::OnABR)
270    EVT_RADIOBUTTON(ID_CBR,    ExportMP3Options::OnCBR)
271    EVT_CHOICE(wxID_ANY,       ExportMP3Options::OnQuality)
272    EVT_CHECKBOX(ID_MONO,      ExportMP3Options::OnMono)
273 END_EVENT_TABLE()
274 
275 ///
276 ///
277 ExportMP3Options::ExportMP3Options(wxWindow *parent, int WXUNUSED(format))
278 :  wxPanelWrapper(parent, wxID_ANY)
279 {
280    mSetRate = gPrefs->Read(wxT("/FileFormats/MP3SetRate"), PRESET_STANDARD);
281    mVbrRate = gPrefs->Read(wxT("/FileFormats/MP3VbrRate"), QUALITY_2);
282    mAbrRate = gPrefs->Read(wxT("/FileFormats/MP3AbrRate"), 192);
283    mCbrRate = gPrefs->Read(wxT("/FileFormats/MP3CbrRate"), 192);
284 
285    ShuttleGui S(this, eIsCreatingFromPrefs);
286    PopulateOrExchange(S);
287 
288    TransferDataToWindow();
289 }
290 
~ExportMP3Options()291 ExportMP3Options::~ExportMP3Options()
292 {
293    TransferDataFromWindow();
294 }
295 
296 EnumSetting< MP3RateMode > MP3RateModeSetting{
297    wxT("/FileFormats/MP3RateModeChoice"),
298    {
299       { wxT("SET"), XXO("Preset") },
300       { wxT("VBR"), XXO("Variable") },
301       { wxT("ABR"), XXO("Average") },
302       { wxT("CBR"), XXO("Constant") },
303    },
304    0, // MODE_SET
305 
306    // for migrating old preferences:
307    {
308       MODE_SET, MODE_VBR, MODE_ABR, MODE_CBR
309    },
310    wxT("/FileFormats/MP3RateMode"),
311 };
312 
313 static EnumSetting< MP3ChannelMode > MP3ChannelModeSetting{
314    wxT("/FileFormats/MP3ChannelModeChoice"),
315    {
316       EnumValueSymbol{ wxT("JOINT"), XXO("Joint Stereo") },
317       EnumValueSymbol{ wxT("STEREO"), XXO("Stereo") },
318    },
319    0, // CHANNEL_JOINT
320 
321    // for migrating old preferences:
322    {
323       CHANNEL_JOINT, CHANNEL_STEREO,
324    },
325    wxT("/FileFormats/MP3ChannelMode"),
326 };
327 
328 ///
329 ///
PopulateOrExchange(ShuttleGui & S)330 void ExportMP3Options::PopulateOrExchange(ShuttleGui & S)
331 {
332    bool mono = false;
333    gPrefs->Read(wxT("/FileFormats/MP3ForceMono"), &mono, 0);
334 
335    const TranslatableStrings *choices = nullptr;
336    const std::vector< int > *codes = nullptr;
337    //bool enable;
338    int defrate;
339 
340    S.StartVerticalLay();
341    {
342       S.StartHorizontalLay(wxCENTER);
343       {
344          S.StartMultiColumn(2, wxCENTER);
345          {
346             S.SetStretchyCol(1);
347             S.StartTwoColumn();
348             {
349                S.AddPrompt(XXO("Bit Rate Mode:"));
350 
351                // Bug 2692: Place button group in panel so tabbing will work and,
352                // on the Mac, VoiceOver will announce as radio buttons.
353                S.StartPanel();
354                {
355                   S.StartHorizontalLay();
356                   {
357                      S.StartRadioButtonGroup(MP3RateModeSetting);
358                      {
359                         mSET = S.Id(ID_SET).TieRadioButton();
360                         mVBR = S.Id(ID_VBR).TieRadioButton();
361                         mABR = S.Id(ID_ABR).TieRadioButton();
362                         mCBR = S.Id(ID_CBR).TieRadioButton();
363                      }
364                      S.EndRadioButtonGroup();
365                   }
366                   S.EndHorizontalLay();
367                }
368                S.EndPanel();
369 
370                /* PRL: unfortunately this bit of procedural code must
371                 interrupt the mostly-declarative dialog description, until
372                 we find a better solution.  Because when shuttling values
373                 from the dialog, we must shuttle out the MP3RateModeSetting
374                 first. */
375 
376                switch( MP3RateModeSetting.ReadEnum() ) {
377                   case MODE_SET:
378                      choices = &setRateNames;
379                      //enable = true;
380                      defrate = mSetRate;
381                      break;
382 
383                   case MODE_VBR:
384                      choices = &varRateNames;
385                      //enable = true;
386                      defrate = mVbrRate;
387                      break;
388 
389                   case MODE_ABR:
390                      choices = &fixRateNames;
391                      codes = &fixRateValues;
392                      //enable = false;
393                      defrate = mAbrRate;
394                      break;
395 
396                   case MODE_CBR:
397                   default:
398                      choices = &fixRateNames;
399                      codes = &fixRateValues;
400                      //enable = false;
401                      defrate = mCbrRate;
402                      break;
403                }
404 
405                mRate = S.Id(ID_QUALITY).TieNumberAsChoice(
406                   XXO("Quality"),
407                   { wxT("/FileFormats/MP3Bitrate"), defrate },
408                   *choices,
409                   codes
410                );
411                /*
412                mMode = S.Disable(!enable)
413                   .TieNumberAsChoice(
414                      XXO("Variable Speed:"),
415                      { wxT("/FileFormats/MP3VarMode"), ROUTINE_FAST },
416                      varModeNames );
417                */
418                S.AddPrompt(XXO("Channel Mode:"));
419                S.StartMultiColumn(2, wxEXPAND);
420                {
421                   // Bug 2692: Place button group in panel so tabbing will work and,
422                   // on the Mac, VoiceOver will announce as radio buttons.
423                   S.StartPanel();
424                   {
425                      S.StartHorizontalLay();
426                      {
427                         S.StartRadioButtonGroup(MP3ChannelModeSetting);
428                         {
429                            mJoint = S.Disable(mono)
430                               .TieRadioButton();
431                            mStereo = S.Disable(mono)
432                               .TieRadioButton();
433                         }
434                         S.EndRadioButtonGroup();
435                      }
436                      S.EndHorizontalLay();
437                   }
438                   S.EndPanel();
439 
440                   mMono = S.Id(ID_MONO).AddCheckBox(XXO("Force export to mono"), mono);
441                }
442                S.EndMultiColumn();
443             }
444             S.EndTwoColumn();
445          }
446          S.EndMultiColumn();
447       }
448       S.EndHorizontalLay();
449    }
450    S.EndVerticalLay();
451 }
452 
453 ///
454 ///
TransferDataToWindow()455 bool ExportMP3Options::TransferDataToWindow()
456 {
457    return true;
458 }
459 
TransferDataFromWindow()460 bool ExportMP3Options::TransferDataFromWindow()
461 {
462    ShuttleGui S(this, eIsSavingToPrefs);
463    PopulateOrExchange(S);
464 
465    gPrefs->Write(wxT("/FileFormats/MP3SetRate"), mSetRate);
466    gPrefs->Write(wxT("/FileFormats/MP3VbrRate"), mVbrRate);
467    gPrefs->Write(wxT("/FileFormats/MP3AbrRate"), mAbrRate);
468    gPrefs->Write(wxT("/FileFormats/MP3CbrRate"), mCbrRate);
469    gPrefs->Flush();
470 
471    return true;
472 }
473 
474 namespace {
475 
ValidateValue(int nValues,int value,int defaultValue)476 int ValidateValue( int nValues, int value, int defaultValue )
477 {
478    return (value >= 0 && value < nValues) ? value : defaultValue;
479 }
480 
ValidateValue(const std::vector<int> & values,int value,int defaultValue)481 int ValidateValue( const std::vector<int> &values, int value, int defaultValue )
482 {
483    auto start = values.begin(), finish = values.end(),
484       iter = std::find( start, finish, value );
485    return ( iter != finish ) ? value : defaultValue;
486 }
487 
ValidateIndex(const std::vector<int> & values,int value,int defaultIndex)488 int ValidateIndex( const std::vector<int> &values, int value, int defaultIndex )
489 {
490    auto start = values.begin(), finish = values.end(),
491       iter = std::find( start, finish, value );
492    return ( iter != finish ) ? static_cast<int>( iter - start ) : defaultIndex;
493 }
494 
495 }
496 
497 ///
498 ///
OnSET(wxCommandEvent & WXUNUSED (event))499 void ExportMP3Options::OnSET(wxCommandEvent& WXUNUSED(event))
500 {
501    LoadNames(setRateNames);
502 
503    mRate->SetSelection(ValidateValue(setRateNames.size(), mSetRate, 2));
504    mRate->Refresh();
505    //mMode->Enable(true);
506 }
507 
508 ///
509 ///
OnVBR(wxCommandEvent & WXUNUSED (event))510 void ExportMP3Options::OnVBR(wxCommandEvent& WXUNUSED(event))
511 {
512    LoadNames(varRateNames);
513 
514    mRate->SetSelection(ValidateValue(varRateNames.size(), mVbrRate, 2));
515    mRate->Refresh();
516    //mMode->Enable(true);
517 }
518 
519 ///
520 ///
OnABR(wxCommandEvent & WXUNUSED (event))521 void ExportMP3Options::OnABR(wxCommandEvent& WXUNUSED(event))
522 {
523    LoadNames(fixRateNames);
524 
525    mRate->SetSelection(ValidateIndex(fixRateValues, mAbrRate, 10));
526    mRate->Refresh();
527    //mMode->Enable(false);
528 }
529 
530 ///
531 ///
OnCBR(wxCommandEvent & WXUNUSED (event))532 void ExportMP3Options::OnCBR(wxCommandEvent& WXUNUSED(event))
533 {
534    LoadNames(fixRateNames);
535 
536    mRate->SetSelection(ValidateIndex(fixRateValues, mCbrRate, 10));
537    mRate->Refresh();
538    //mMode->Enable(false);
539 }
540 
OnQuality(wxCommandEvent & WXUNUSED (event))541 void ExportMP3Options::OnQuality(wxCommandEvent& WXUNUSED(event))
542 {
543    int sel = mRate->GetSelection();
544 
545    if (mSET->GetValue()) {
546       mSetRate = sel;
547    }
548    else if (mVBR->GetValue()) {
549       mVbrRate = sel;
550    }
551    else if (mABR->GetValue()) {
552       mAbrRate = fixRateValues[ sel ];
553    }
554    else {
555       mCbrRate = fixRateValues[ sel ];
556    }
557 }
558 
OnMono(wxCommandEvent &)559 void ExportMP3Options::OnMono(wxCommandEvent& /*evt*/)
560 {
561    bool mono = false;
562    mono = mMono->GetValue();
563    mJoint->Enable(!mono);
564    mStereo->Enable(!mono);
565 
566    gPrefs->Write(wxT("/FileFormats/MP3ForceMono"), mono);
567    gPrefs->Flush();
568 }
569 
LoadNames(const TranslatableStrings & names)570 void ExportMP3Options::LoadNames(const TranslatableStrings &names)
571 {
572    mRate->Clear();
573    for (const auto &name : names)
574       mRate->Append( name.Translation() );
575 }
576 
577 //----------------------------------------------------------------------------
578 // FindDialog
579 //----------------------------------------------------------------------------
580 
581 #define ID_BROWSE 5000
582 #define ID_DLOAD  5001
583 
584 class FindDialog final : public wxDialogWrapper
585 {
586 public:
587 
588 #ifndef DISABLE_DYNAMIC_LOADING_LAME
589 
FindDialog(wxWindow * parent,wxString path,wxString name,FileNames::FileTypes types)590    FindDialog(wxWindow *parent, wxString path, wxString name,
591       FileNames::FileTypes types)
592    :  wxDialogWrapper(parent, wxID_ANY,
593    /* i18n-hint: LAME is the name of an MP3 converter and should not be translated*/
594    XO("Locate LAME"))
595    {
596       SetName();
597       ShuttleGui S(this, eIsCreating);
598 
599       mPath = path;
600       mName = name;
601       mTypes = std::move( types );
602 
603       mLibPath.Assign(mPath, mName);
604 
605       PopulateOrExchange(S);
606    }
607 
PopulateOrExchange(ShuttleGui & S)608    void PopulateOrExchange(ShuttleGui & S)
609    {
610       S.SetBorder(10);
611       S.StartVerticalLay(true);
612       {
613          S.AddTitle(
614             XO("Audacity needs the file %s to create MP3s.")
615                .Format( mName ) );
616 
617          S.SetBorder(3);
618          S.StartHorizontalLay(wxALIGN_LEFT, true);
619          {
620             S.AddTitle( XO("Location of %s:").Format( mName ) );
621          }
622          S.EndHorizontalLay();
623 
624          S.StartMultiColumn(2, wxEXPAND);
625          S.SetStretchyCol(0);
626          {
627             if (mLibPath.GetFullPath().empty()) {
628                mPathText = S.AddTextBox( {},
629                   /* i18n-hint: There is a  button to the right of the arrow.*/
630                   wxString::Format(_("To find %s, click here -->"), mName), 0);
631             }
632             else {
633                mPathText = S.AddTextBox( {}, mLibPath.GetFullPath(), 0);
634             }
635             S.Id(ID_BROWSE).AddButton(XXO("Browse..."), wxALIGN_RIGHT);
636             S.AddVariableText(
637                /* i18n-hint: There is a  button to the right of the arrow.*/
638                XO("To get a free copy of LAME, click here -->"), true);
639             /* i18n-hint: (verb)*/
640             S.Id(ID_DLOAD).AddButton(XXO("Download"), wxALIGN_RIGHT);
641          }
642          S.EndMultiColumn();
643 
644          S.AddStandardButtons();
645       }
646       S.EndVerticalLay();
647 
648       Layout();
649       Fit();
650       SetMinSize(GetSize());
651       Center();
652 
653       return;
654    }
655 
OnBrowse(wxCommandEvent & WXUNUSED (event))656    void OnBrowse(wxCommandEvent & WXUNUSED(event))
657    {
658       /* i18n-hint: It's asking for the location of a file, for
659        * example, "Where is lame_enc.dll?" - you could translate
660        * "Where would I find the file %s" instead if you want. */
661       auto question = XO("Where is %s?").Format( mName );
662 
663       wxString path = SelectFile(FileNames::Operation::_None,
664          question,
665             mLibPath.GetPath(),
666             mLibPath.GetName(),
667             wxT(""),
668             mTypes,
669             wxFD_OPEN | wxRESIZE_BORDER,
670             this);
671       if (!path.empty()) {
672          mLibPath = path;
673          mPathText->SetValue(path);
674       }
675    }
676 
OnDownload(wxCommandEvent & WXUNUSED (event))677    void OnDownload(wxCommandEvent & WXUNUSED(event))
678    {
679       HelpSystem::ShowHelp(this, L"FAQ:Installing_the_LAME_MP3_Encoder");
680    }
681 
GetLibPath()682    wxString GetLibPath()
683    {
684       return mLibPath.GetFullPath();
685    }
686 
687 #endif // DISABLE_DYNAMIC_LOADING_LAME
688 
689 private:
690 
691 #ifndef DISABLE_DYNAMIC_LOADING_LAME
692    wxFileName mLibPath;
693 
694    wxString mPath;
695    wxString mName;
696    FileNames::FileTypes mTypes;
697 #endif // DISABLE_DYNAMIC_LOADING_LAME
698 
699    wxTextCtrl *mPathText;
700 
701    DECLARE_EVENT_TABLE()
702 };
703 
704 #ifndef DISABLE_DYNAMIC_LOADING_LAME
705 BEGIN_EVENT_TABLE(FindDialog, wxDialogWrapper)
706    EVT_BUTTON(ID_BROWSE, FindDialog::OnBrowse)
707    EVT_BUTTON(ID_DLOAD,  FindDialog::OnDownload)
708 END_EVENT_TABLE()
709 #endif // DISABLE_DYNAMIC_LOADING_LAME
710 
711 //----------------------------------------------------------------------------
712 // MP3Exporter
713 //----------------------------------------------------------------------------
714 
715 #ifndef DISABLE_DYNAMIC_LOADING_LAME
716 
717 typedef lame_global_flags *lame_init_t(void);
718 typedef int lame_init_params_t(lame_global_flags*);
719 typedef const char* get_lame_version_t(void);
720 
721 typedef int CDECL lame_encode_buffer_ieee_float_t(
722       lame_t          gfp,
723       const float     pcm_l[],
724       const float     pcm_r[],
725       const int       nsamples,
726       unsigned char * mp3buf,
727       const int       mp3buf_size);
728 
729 typedef int CDECL lame_encode_buffer_interleaved_ieee_float_t(
730       lame_t          gfp,
731       const float     pcm[],
732       const int       nsamples,
733       unsigned char * mp3buf,
734       const int       mp3buf_size);
735 
736 typedef int lame_encode_flush_t(
737       lame_global_flags *gf,
738       unsigned char*     mp3buf,
739       int                size );
740 
741 typedef int lame_close_t(lame_global_flags*);
742 
743 typedef int lame_set_in_samplerate_t(lame_global_flags*, int);
744 typedef int lame_set_out_samplerate_t(lame_global_flags*, int);
745 typedef int lame_set_num_channels_t(lame_global_flags*, int );
746 typedef int lame_set_quality_t(lame_global_flags*, int);
747 typedef int lame_set_brate_t(lame_global_flags*, int);
748 typedef int lame_set_VBR_t(lame_global_flags *, vbr_mode);
749 typedef int lame_set_VBR_q_t(lame_global_flags *, int);
750 typedef int lame_set_VBR_min_bitrate_kbps_t(lame_global_flags *, int);
751 typedef int lame_set_mode_t(lame_global_flags *, MPEG_mode);
752 typedef int lame_set_preset_t(lame_global_flags *, int);
753 typedef int lame_set_error_protection_t(lame_global_flags *, int);
754 typedef int lame_set_disable_reservoir_t(lame_global_flags *, int);
755 typedef int lame_set_bWriteVbrTag_t(lame_global_flags *, int);
756 typedef size_t lame_get_lametag_frame_t(const lame_global_flags *, unsigned char* buffer, size_t size);
757 typedef void lame_mp3_tags_fid_t(lame_global_flags *, FILE *);
758 
759 #endif // DISABLE_DYNAMIC_LOADING_LAME
760 
761 #if defined(__WXMSW__)
762 // An alternative solution to give Windows an additional chance of writing the tag before
763 // falling bato to lame_mp3_tag_fid().  The latter can have DLL sharing issues when mixing
764 // Debug/Release builds of Audacity and the lame DLL.
765 typedef unsigned long beWriteInfoTag_t(lame_global_flags *, char *);
766 
767 // We use this to determine if the user has selected an older, Blade API only, lame_enc.dll
768 // so we can be more specific about why their library isn't acceptable.
769 typedef struct	{
770 
771    // BladeEnc DLL Version number
772 
773    BYTE	byDLLMajorVersion;
774    BYTE	byDLLMinorVersion;
775 
776    // BladeEnc Engine Version Number
777 
778    BYTE	byMajorVersion;
779    BYTE	byMinorVersion;
780 
781    // DLL Release date
782 
783    BYTE	byDay;
784    BYTE	byMonth;
785    WORD	wYear;
786 
787    // BladeEnc	Homepage URL
788 
789    CHAR	zHomepage[129];
790 
791    BYTE	byAlphaLevel;
792    BYTE	byBetaLevel;
793    BYTE	byMMXEnabled;
794 
795    BYTE	btReserved[125];
796 } be_version;
797 typedef void beVersion_t(be_version *);
798 #endif
799 
800 class MP3Exporter
801 {
802 public:
803    enum AskUser
804    {
805       No,
806       Maybe,
807       Yes
808    };
809 
810    MP3Exporter();
811    ~MP3Exporter();
812 
813 #ifndef DISABLE_DYNAMIC_LOADING_LAME
814    bool FindLibrary(wxWindow *parent);
815    bool LoadLibrary(wxWindow *parent, AskUser askuser);
816    bool ValidLibraryLoaded();
817 #endif // DISABLE_DYNAMIC_LOADING_LAME
818 
819    /* These global settings keep state over the life of the object */
820    void SetMode(int mode);
821    void SetBitrate(int rate);
822    void SetQuality(int q/*, int r*/);
823    void SetChannel(int mode);
824 
825    /* Virtual methods that must be supplied by library interfaces */
826 
827    /* initialize the library interface */
828    bool InitLibrary(wxString libpath);
829    bool InitLibraryInternal();
830    bool InitLibraryExternal(wxString libpath);
831    void FreeLibrary();
832 
833    /* get library info */
834    wxString GetLibraryVersion();
835    wxString GetLibraryName();
836    wxString GetLibraryPath();
837    FileNames::FileTypes GetLibraryTypes();
838 
839    /* returns the number of samples PER CHANNEL to send for each call to EncodeBuffer */
840    int InitializeStream(unsigned channels, int sampleRate);
841 
842    /* In bytes. must be called AFTER InitializeStream */
843    int GetOutBufferSize();
844 
845    /* returns the number of bytes written. input is interleaved if stereo*/
846    int EncodeBuffer(float inbuffer[], unsigned char outbuffer[]);
847    int EncodeRemainder(float inbuffer[], int nSamples,
848                        unsigned char outbuffer[]);
849 
850    int EncodeBufferMono(float inbuffer[], unsigned char outbuffer[]);
851    int EncodeRemainderMono(float inbuffer[], int nSamples,
852                            unsigned char outbuffer[]);
853 
854    int FinishStream(unsigned char outbuffer[]);
855    void CancelEncoding();
856 
857    bool PutInfoTag(wxFFile & f, wxFileOffset off);
858 
859 private:
860    bool mLibIsExternal;
861 
862 #ifndef DISABLE_DYNAMIC_LOADING_LAME
863    wxString mLibPath;
864    wxDynamicLibrary lame_lib;
865    bool mLibraryLoaded;
866 #endif // DISABLE_DYNAMIC_LOADING_LAME
867 
868 #if defined(__WXMSW__)
869    TranslatableString mBladeVersion;
870 #endif
871 
872    bool mEncoding;
873    int mMode;
874    int mBitrate;
875    int mQuality;
876    //int mRoutine;
877    int mChannel;
878 
879 #ifndef DISABLE_DYNAMIC_LOADING_LAME
880    /* function pointers to the symbols we get from the library */
881    lame_init_t* lame_init;
882    lame_init_params_t* lame_init_params;
883    lame_encode_buffer_ieee_float_t* lame_encode_buffer_ieee_float;
884    lame_encode_buffer_interleaved_ieee_float_t* lame_encode_buffer_interleaved_ieee_float;
885    lame_encode_flush_t* lame_encode_flush;
886    lame_close_t* lame_close;
887    get_lame_version_t* get_lame_version;
888 
889    lame_set_in_samplerate_t* lame_set_in_samplerate;
890    lame_set_out_samplerate_t* lame_set_out_samplerate;
891    lame_set_num_channels_t* lame_set_num_channels;
892    lame_set_quality_t* lame_set_quality;
893    lame_set_brate_t* lame_set_brate;
894    lame_set_VBR_t* lame_set_VBR;
895    lame_set_VBR_q_t* lame_set_VBR_q;
896    lame_set_VBR_min_bitrate_kbps_t* lame_set_VBR_min_bitrate_kbps;
897    lame_set_mode_t* lame_set_mode;
898    lame_set_preset_t* lame_set_preset;
899    lame_set_error_protection_t* lame_set_error_protection;
900    lame_set_disable_reservoir_t *lame_set_disable_reservoir;
901    lame_set_bWriteVbrTag_t *lame_set_bWriteVbrTag;
902    lame_get_lametag_frame_t *lame_get_lametag_frame;
903    lame_mp3_tags_fid_t *lame_mp3_tags_fid;
904 #if defined(__WXMSW__)
905    beWriteInfoTag_t *beWriteInfoTag;
906    beVersion_t *beVersion;
907 #endif
908 #endif // DISABLE_DYNAMIC_LOADING_LAME
909 
910    lame_global_flags *mGF;
911 
912    static const int mSamplesPerChunk = 220500;
913    // See lame.h/lame_encode_buffer() for further explanation
914    // As coded here, this should be the worst case.
915    static const int mOutBufferSize =
916       mSamplesPerChunk * (320 / 8) / 8 + 4 * 1152 * (320 / 8) / 8 + 512;
917 
918    // See MAXFRAMESIZE in libmp3lame/VbrTag.c for explanation of 2880.
919    unsigned char mInfoTagBuf[2880];
920    size_t mInfoTagLen;
921 };
922 
MP3Exporter()923 MP3Exporter::MP3Exporter()
924 {
925 // We could use #defines rather than this variable.
926 // The idea of the variable is that if we wanted, we could allow
927 // a dynamic override of the library, e.g. with a newer faster version,
928 // or to fix CVEs in the underlying librray.
929 // for now though the 'variable' is a constant.
930 #ifdef MP3_EXPORT_BUILT_IN
931    mLibIsExternal = false;
932 #else
933    mLibIsExternal = true;
934 #endif
935 
936 #ifndef DISABLE_DYNAMIC_LOADING_LAME
937    mLibraryLoaded = false;
938 #endif // DISABLE_DYNAMIC_LOADING_LAME
939    mEncoding = false;
940    mGF = NULL;
941 
942 #ifndef DISABLE_DYNAMIC_LOADING_LAME
943    if (gPrefs) {
944       mLibPath = gPrefs->Read(wxT("/MP3/MP3LibPath"), wxT(""));
945    }
946 #endif // DISABLE_DYNAMIC_LOADING_LAME
947 
948    mBitrate = 128;
949    mQuality = QUALITY_2;
950    mChannel = CHANNEL_STEREO;
951    mMode = MODE_CBR;
952    //mRoutine = ROUTINE_FAST;
953 }
954 
~MP3Exporter()955 MP3Exporter::~MP3Exporter()
956 {
957    FreeLibrary();
958 }
959 
960 #ifndef DISABLE_DYNAMIC_LOADING_LAME
961 
FindLibrary(wxWindow * parent)962 bool MP3Exporter::FindLibrary(wxWindow *parent)
963 {
964    wxString path;
965    wxString name;
966 
967    if (!mLibPath.empty()) {
968       wxFileName fn = mLibPath;
969       path = fn.GetPath();
970       name = fn.GetFullName();
971    }
972    else {
973       path = GetLibraryPath();
974       name = GetLibraryName();
975    }
976 
977    FindDialog fd(parent,
978       path,
979       name,
980       GetLibraryTypes());
981 
982    if (fd.ShowModal() == wxID_CANCEL) {
983       return false;
984    }
985 
986    path = fd.GetLibPath();
987 
988    if (!::wxFileExists(path)) {
989       return false;
990    }
991 
992    mLibPath = path;
993 
994    return (gPrefs->Write(wxT("/MP3/MP3LibPath"), mLibPath) && gPrefs->Flush());
995 }
996 
LoadLibrary(wxWindow * parent,AskUser askuser)997 bool MP3Exporter::LoadLibrary(wxWindow *parent, AskUser askuser)
998 {
999 
1000    if (ValidLibraryLoaded()) {
1001       FreeLibrary();
1002       mLibraryLoaded = false;
1003    }
1004 
1005 #if defined(__WXMSW__)
1006    mBladeVersion = {};
1007 #endif
1008 
1009    if( !mLibIsExternal ){
1010       mLibraryLoaded = InitLibraryInternal();
1011       return mLibraryLoaded;
1012    }
1013 
1014    // First try loading it from a previously located path
1015    if (!mLibPath.empty()) {
1016       wxLogMessage(wxT("Attempting to load LAME from previously defined path"));
1017       mLibraryLoaded = InitLibrary(mLibPath);
1018    }
1019 
1020    // If not successful, try loading using system search paths
1021    if (!ValidLibraryLoaded()) {
1022       wxLogMessage(wxT("Attempting to load LAME from system search paths"));
1023       mLibPath = GetLibraryName();
1024       mLibraryLoaded = InitLibrary(mLibPath);
1025    }
1026 
1027    // If not successful, try loading using compiled in path
1028    if (!ValidLibraryLoaded()) {
1029       wxLogMessage(wxT("Attempting to load LAME from builtin path"));
1030       wxFileName fn(GetLibraryPath(), GetLibraryName());
1031       mLibPath = fn.GetFullPath();
1032       mLibraryLoaded = InitLibrary(mLibPath);
1033    }
1034 
1035    // If not successful, must ask the user
1036    if (!ValidLibraryLoaded()) {
1037       wxLogMessage(wxT("(Maybe) ask user for library"));
1038       if (askuser == MP3Exporter::Maybe && FindLibrary(parent)) {
1039          mLibraryLoaded = InitLibrary(mLibPath);
1040       }
1041    }
1042 
1043    // Oh well, just give up
1044    if (!ValidLibraryLoaded()) {
1045 #if defined(__WXMSW__)
1046       if (askuser && !mBladeVersion.empty()) {
1047          AudacityMessageBox( mBladeVersion );
1048       }
1049 #endif
1050       wxLogMessage(wxT("Failed to locate LAME library"));
1051 
1052       return false;
1053    }
1054 
1055    wxLogMessage(wxT("LAME library successfully loaded"));
1056 
1057    return true;
1058 }
1059 
ValidLibraryLoaded()1060 bool MP3Exporter::ValidLibraryLoaded()
1061 {
1062    return mLibraryLoaded;
1063 }
1064 
1065 #endif // DISABLE_DYNAMIC_LOADING_LAME
1066 
SetMode(int mode)1067 void MP3Exporter::SetMode(int mode)
1068 {
1069    mMode = mode;
1070 }
1071 
SetBitrate(int rate)1072 void MP3Exporter::SetBitrate(int rate)
1073 {
1074    mBitrate = rate;
1075 }
1076 
SetQuality(int q)1077 void MP3Exporter::SetQuality(int q/*, int r*/)
1078 {
1079    mQuality = q;
1080    //mRoutine = r;
1081 }
1082 
SetChannel(int mode)1083 void MP3Exporter::SetChannel(int mode)
1084 {
1085    mChannel = mode;
1086 }
1087 
InitLibrary(wxString libpath)1088 bool MP3Exporter::InitLibrary(wxString libpath)
1089 {
1090    return mLibIsExternal ? InitLibraryExternal(libpath) : InitLibraryInternal();
1091 }
1092 
InitLibraryInternal()1093 bool MP3Exporter::InitLibraryInternal()
1094 {
1095    wxLogMessage(wxT("Using internal LAME"));
1096 
1097 // The global ::lame_something symbols only exist if LAME is built in.
1098 // So we don't reference them unless they are.
1099 #ifdef MP3_EXPORT_BUILT_IN
1100 
1101    lame_init = ::lame_init;
1102    get_lame_version = ::get_lame_version;
1103    lame_init_params = ::lame_init_params;
1104    lame_encode_buffer_ieee_float = ::lame_encode_buffer_ieee_float;
1105    lame_encode_buffer_interleaved_ieee_float = ::lame_encode_buffer_interleaved_ieee_float;
1106    lame_encode_flush = ::lame_encode_flush;
1107    lame_close = ::lame_close;
1108 
1109    lame_set_in_samplerate = ::lame_set_in_samplerate;
1110    lame_set_out_samplerate = ::lame_set_out_samplerate;
1111    lame_set_num_channels = ::lame_set_num_channels;
1112    lame_set_quality = ::lame_set_quality;
1113    lame_set_brate = ::lame_set_brate;
1114    lame_set_VBR = ::lame_set_VBR;
1115    lame_set_VBR_q = ::lame_set_VBR_q;
1116    lame_set_VBR_min_bitrate_kbps = ::lame_set_VBR_min_bitrate_kbps;
1117    lame_set_mode = ::lame_set_mode;
1118    lame_set_preset = ::lame_set_preset;
1119    lame_set_error_protection = ::lame_set_error_protection;
1120    lame_set_disable_reservoir = ::lame_set_disable_reservoir;
1121    lame_set_bWriteVbrTag = ::lame_set_bWriteVbrTag;
1122 
1123    // These are optional
1124    //lame_get_lametag_frame = ::lame_get_lametag_frame;
1125    lame_get_lametag_frame = NULL;
1126    lame_mp3_tags_fid = ::lame_mp3_tags_fid;
1127 
1128 #if defined(__WXMSW__)
1129    //beWriteInfoTag = ::beWriteInfoTag;
1130    //beVersion = ::beVersion;
1131    beWriteInfoTag = NULL;
1132    beVersion = NULL;
1133 #endif
1134 
1135    mGF = lame_init();
1136    if (mGF == NULL) {
1137       return false;
1138    }
1139 #endif
1140 
1141    return true;
1142 }
1143 
1144 
InitLibraryExternal(wxString libpath)1145 bool MP3Exporter::InitLibraryExternal(wxString libpath)
1146 {
1147    wxLogMessage(wxT("Loading LAME from %s"), libpath);
1148 
1149 #ifndef DISABLE_DYNAMIC_LOADING_LAME
1150    if (!lame_lib.Load(libpath, wxDL_LAZY)) {
1151       wxLogMessage(wxT("load failed"));
1152       return false;
1153    }
1154 
1155    wxLogMessage(wxT("Actual LAME path %s"),
1156               FileNames::PathFromAddr(lame_lib.GetSymbol(wxT("lame_init"))));
1157 
1158    lame_init = (lame_init_t *)
1159       lame_lib.GetSymbol(wxT("lame_init"));
1160    get_lame_version = (get_lame_version_t *)
1161       lame_lib.GetSymbol(wxT("get_lame_version"));
1162    lame_init_params = (lame_init_params_t *)
1163       lame_lib.GetSymbol(wxT("lame_init_params"));
1164    lame_encode_buffer_ieee_float = (lame_encode_buffer_ieee_float_t *)
1165       lame_lib.GetSymbol(wxT("lame_encode_buffer_ieee_float"));
1166    lame_encode_buffer_interleaved_ieee_float = (lame_encode_buffer_interleaved_ieee_float_t *)
1167       lame_lib.GetSymbol(wxT("lame_encode_buffer_interleaved_ieee_float"));
1168    lame_encode_flush = (lame_encode_flush_t *)
1169       lame_lib.GetSymbol(wxT("lame_encode_flush"));
1170    lame_close = (lame_close_t *)
1171       lame_lib.GetSymbol(wxT("lame_close"));
1172 
1173    lame_set_in_samplerate = (lame_set_in_samplerate_t *)
1174        lame_lib.GetSymbol(wxT("lame_set_in_samplerate"));
1175    lame_set_out_samplerate = (lame_set_out_samplerate_t *)
1176        lame_lib.GetSymbol(wxT("lame_set_out_samplerate"));
1177    lame_set_num_channels = (lame_set_num_channels_t *)
1178        lame_lib.GetSymbol(wxT("lame_set_num_channels"));
1179    lame_set_quality = (lame_set_quality_t *)
1180        lame_lib.GetSymbol(wxT("lame_set_quality"));
1181    lame_set_brate = (lame_set_brate_t *)
1182        lame_lib.GetSymbol(wxT("lame_set_brate"));
1183    lame_set_VBR = (lame_set_VBR_t *)
1184        lame_lib.GetSymbol(wxT("lame_set_VBR"));
1185    lame_set_VBR_q = (lame_set_VBR_q_t *)
1186        lame_lib.GetSymbol(wxT("lame_set_VBR_q"));
1187    lame_set_VBR_min_bitrate_kbps = (lame_set_VBR_min_bitrate_kbps_t *)
1188        lame_lib.GetSymbol(wxT("lame_set_VBR_min_bitrate_kbps"));
1189    lame_set_mode = (lame_set_mode_t *)
1190        lame_lib.GetSymbol(wxT("lame_set_mode"));
1191    lame_set_preset = (lame_set_preset_t *)
1192        lame_lib.GetSymbol(wxT("lame_set_preset"));
1193    lame_set_error_protection = (lame_set_error_protection_t *)
1194        lame_lib.GetSymbol(wxT("lame_set_error_protection"));
1195    lame_set_disable_reservoir = (lame_set_disable_reservoir_t *)
1196        lame_lib.GetSymbol(wxT("lame_set_disable_reservoir"));
1197    lame_set_bWriteVbrTag = (lame_set_bWriteVbrTag_t *)
1198        lame_lib.GetSymbol(wxT("lame_set_bWriteVbrTag"));
1199 
1200    // These are optional
1201    lame_get_lametag_frame = (lame_get_lametag_frame_t *)
1202        lame_lib.GetSymbol(wxT("lame_get_lametag_frame"));
1203    lame_mp3_tags_fid = (lame_mp3_tags_fid_t *)
1204        lame_lib.GetSymbol(wxT("lame_mp3_tags_fid"));
1205 #if defined(__WXMSW__)
1206    beWriteInfoTag = (beWriteInfoTag_t *)
1207        lame_lib.GetSymbol(wxT("beWriteInfoTag"));
1208    beVersion = (beVersion_t *)
1209        lame_lib.GetSymbol(wxT("beVersion"));
1210 #endif
1211 
1212    if (!lame_init ||
1213       !get_lame_version ||
1214       !lame_init_params ||
1215       !lame_encode_buffer_ieee_float ||
1216       !lame_encode_buffer_interleaved_ieee_float ||
1217       !lame_encode_flush ||
1218       !lame_close ||
1219       !lame_set_in_samplerate ||
1220       !lame_set_out_samplerate ||
1221       !lame_set_num_channels ||
1222       !lame_set_quality ||
1223       !lame_set_brate ||
1224       !lame_set_VBR ||
1225       !lame_set_VBR_q ||
1226       !lame_set_mode ||
1227       !lame_set_preset ||
1228       !lame_set_error_protection ||
1229       !lame_set_disable_reservoir ||
1230       !lame_set_bWriteVbrTag)
1231    {
1232       wxLogMessage(wxT("Failed to find a required symbol in the LAME library."));
1233 #if defined(__WXMSW__)
1234       if (beVersion) {
1235          be_version v;
1236          beVersion(&v);
1237 
1238          mBladeVersion = XO(
1239 "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'.")
1240             .Format(
1241                v.byMajorVersion,
1242                v.byMinorVersion,
1243                AUDACITY_VERSION,
1244                AUDACITY_RELEASE,
1245                AUDACITY_REVISION);
1246       }
1247 #endif
1248 
1249       lame_lib.Unload();
1250       return false;
1251    }
1252 #endif // DISABLE_DYNAMIC_LOADING_LAME
1253 
1254    mGF = lame_init();
1255    if (mGF == NULL) {
1256       return false;
1257    }
1258 
1259    return true;
1260 }
1261 
FreeLibrary()1262 void MP3Exporter::FreeLibrary()
1263 {
1264    if (mGF) {
1265       lame_close(mGF);
1266       mGF = NULL;
1267    }
1268 
1269 #ifndef DISABLE_DYNAMIC_LOADING_LAME
1270    lame_lib.Unload();
1271 #endif // DISABLE_DYNAMIC_LOADING_LAME
1272 
1273    return;
1274 }
1275 
GetLibraryVersion()1276 wxString MP3Exporter::GetLibraryVersion()
1277 {
1278 #ifndef DISABLE_DYNAMIC_LOADING_LAME
1279    if (!mLibraryLoaded) {
1280       return wxT("");
1281    }
1282 #endif // DISABLE_DYNAMIC_LOADING_LAME
1283 
1284    return wxString::Format(wxT("LAME %hs"), get_lame_version());
1285 }
1286 
InitializeStream(unsigned channels,int sampleRate)1287 int MP3Exporter::InitializeStream(unsigned channels, int sampleRate)
1288 {
1289 #ifndef DISABLE_DYNAMIC_LOADING_LAME
1290    if (!mLibraryLoaded) {
1291       return -1;
1292    }
1293 #endif // DISABLE_DYNAMIC_LOADING_LAME
1294 
1295    if (channels > 2) {
1296       return -1;
1297    }
1298 
1299    lame_set_error_protection(mGF, false);
1300    lame_set_num_channels(mGF, channels);
1301    lame_set_in_samplerate(mGF, sampleRate);
1302    lame_set_out_samplerate(mGF, sampleRate);
1303    lame_set_disable_reservoir(mGF, false);
1304    // Add the VbrTag for all types.  For ABR/VBR, a Xing tag will be created.
1305    // For CBR, it will be a Lame Info tag.
1306    lame_set_bWriteVbrTag(mGF, true);
1307 
1308    // Set the VBR quality or ABR/CBR bitrate
1309    switch (mMode) {
1310       case MODE_SET:
1311       {
1312          int preset;
1313 
1314          if (mQuality == PRESET_INSANE) {
1315             preset = INSANE;
1316          }
1317          //else if (mRoutine == ROUTINE_FAST) {
1318             else if (mQuality == PRESET_EXTREME) {
1319                preset = EXTREME_FAST;
1320             }
1321             else if (mQuality == PRESET_STANDARD) {
1322                preset = STANDARD_FAST;
1323             }
1324             else {
1325                preset = 1007;    // Not defined until 3.96
1326             }
1327          //}
1328          /*
1329          else {
1330             if (mQuality == PRESET_EXTREME) {
1331                preset = EXTREME;
1332             }
1333             else if (mQuality == PRESET_STANDARD) {
1334                preset = STANDARD;
1335             }
1336             else {
1337                preset = 1006;    // Not defined until 3.96
1338             }
1339          }
1340          */
1341          lame_set_preset(mGF, preset);
1342       }
1343       break;
1344 
1345       case MODE_VBR:
1346          lame_set_VBR(mGF, vbr_mtrh );
1347          lame_set_VBR_q(mGF, mQuality);
1348       break;
1349 
1350       case MODE_ABR:
1351          lame_set_preset(mGF, mBitrate );
1352       break;
1353 
1354       default:
1355          lame_set_VBR(mGF, vbr_off);
1356          lame_set_brate(mGF, mBitrate);
1357       break;
1358    }
1359 
1360    // Set the channel mode
1361    MPEG_mode mode;
1362 
1363    if (channels == 1 || mChannel == CHANNEL_MONO) {
1364       mode = MONO;
1365    }
1366    else if (mChannel == CHANNEL_JOINT) {
1367       mode = JOINT_STEREO;
1368    }
1369    else {
1370       mode = STEREO;
1371    }
1372    lame_set_mode(mGF, mode);
1373 
1374    int rc = lame_init_params(mGF);
1375    if (rc < 0) {
1376       return rc;
1377    }
1378 
1379 #if 0
1380    dump_config(mGF);
1381 #endif
1382 
1383    mInfoTagLen = 0;
1384    mEncoding = true;
1385 
1386    return mSamplesPerChunk;
1387 }
1388 
GetOutBufferSize()1389 int MP3Exporter::GetOutBufferSize()
1390 {
1391    if (!mEncoding)
1392       return -1;
1393 
1394    return mOutBufferSize;
1395 }
1396 
EncodeBuffer(float inbuffer[],unsigned char outbuffer[])1397 int MP3Exporter::EncodeBuffer(float inbuffer[], unsigned char outbuffer[])
1398 {
1399    if (!mEncoding) {
1400       return -1;
1401    }
1402 
1403    return lame_encode_buffer_interleaved_ieee_float(mGF, inbuffer, mSamplesPerChunk,
1404       outbuffer, mOutBufferSize);
1405 }
1406 
EncodeRemainder(float inbuffer[],int nSamples,unsigned char outbuffer[])1407 int MP3Exporter::EncodeRemainder(float inbuffer[], int nSamples,
1408                   unsigned char outbuffer[])
1409 {
1410    if (!mEncoding) {
1411       return -1;
1412    }
1413 
1414    return lame_encode_buffer_interleaved_ieee_float(mGF, inbuffer, nSamples, outbuffer,
1415       mOutBufferSize);
1416 }
1417 
EncodeBufferMono(float inbuffer[],unsigned char outbuffer[])1418 int MP3Exporter::EncodeBufferMono(float inbuffer[], unsigned char outbuffer[])
1419 {
1420    if (!mEncoding) {
1421       return -1;
1422    }
1423 
1424    return lame_encode_buffer_ieee_float(mGF, inbuffer,inbuffer, mSamplesPerChunk,
1425       outbuffer, mOutBufferSize);
1426 }
1427 
EncodeRemainderMono(float inbuffer[],int nSamples,unsigned char outbuffer[])1428 int MP3Exporter::EncodeRemainderMono(float inbuffer[], int nSamples,
1429                   unsigned char outbuffer[])
1430 {
1431    if (!mEncoding) {
1432       return -1;
1433    }
1434 
1435    return lame_encode_buffer_ieee_float(mGF, inbuffer, inbuffer, nSamples, outbuffer,
1436       mOutBufferSize);
1437 }
1438 
FinishStream(unsigned char outbuffer[])1439 int MP3Exporter::FinishStream(unsigned char outbuffer[])
1440 {
1441    if (!mEncoding) {
1442       return -1;
1443    }
1444 
1445    mEncoding = false;
1446 
1447    int result = lame_encode_flush(mGF, outbuffer, mOutBufferSize);
1448 
1449 #if defined(DISABLE_DYNAMIC_LOADING_LAME)
1450    mInfoTagLen = lame_get_lametag_frame(mGF, mInfoTagBuf, sizeof(mInfoTagBuf));
1451 #else
1452    if (lame_get_lametag_frame) {
1453       mInfoTagLen = lame_get_lametag_frame(mGF, mInfoTagBuf, sizeof(mInfoTagBuf));
1454    }
1455 #endif
1456 
1457    return result;
1458 }
1459 
CancelEncoding()1460 void MP3Exporter::CancelEncoding()
1461 {
1462    mEncoding = false;
1463 }
1464 
PutInfoTag(wxFFile & f,wxFileOffset off)1465 bool MP3Exporter::PutInfoTag(wxFFile & f, wxFileOffset off)
1466 {
1467    if (mGF) {
1468       if (mInfoTagLen > 0) {
1469          // FIXME: TRAP_ERR Seek and writ ein MP3 exporter could fail.
1470          if ( !f.Seek(off, wxFromStart))
1471             return false;
1472          if (mInfoTagLen > f.Write(mInfoTagBuf, mInfoTagLen))
1473             return false;
1474       }
1475 #if defined(__WXMSW__)
1476       else if (beWriteInfoTag) {
1477          if ( !f.Flush() )
1478             return false;
1479          // PRL:  What is the correct error check on the return value?
1480          beWriteInfoTag(mGF, OSOUTPUT(f.GetName()));
1481          mGF = NULL;
1482       }
1483 #endif
1484       else if (lame_mp3_tags_fid != NULL) {
1485          lame_mp3_tags_fid(mGF, f.fp());
1486       }
1487    }
1488 
1489    if ( !f.SeekEnd() )
1490       return false;
1491 
1492    return true;
1493 }
1494 
1495 #if defined(__WXMSW__)
1496 /* values for Windows */
1497 
GetLibraryPath()1498 wxString MP3Exporter::GetLibraryPath()
1499 {
1500    wxRegKey reg(wxT("HKEY_LOCAL_MACHINE\\Software\\Lame for Audacity"));
1501    wxString path;
1502 
1503    if (reg.Exists()) {
1504       wxLogMessage(wxT("LAME registry key exists."));
1505       reg.QueryValue(wxT("InstallPath"), path);
1506    }
1507    else {
1508       wxLogMessage(wxT("LAME registry key does not exist."));
1509    }
1510 
1511    wxLogMessage(wxT("Library path is: ") + path);
1512 
1513    return path;
1514 }
1515 
GetLibraryName()1516 wxString MP3Exporter::GetLibraryName()
1517 {
1518    return wxT("lame_enc.dll");
1519 }
1520 
GetLibraryTypes()1521 FileNames::FileTypes MP3Exporter::GetLibraryTypes()
1522 {
1523    return {
1524       { XO("Only lame_enc.dll"), { wxT("lame_enc.dll") } },
1525       FileNames::DynamicLibraries,
1526       FileNames::AllFiles
1527    };
1528 }
1529 
1530 #elif defined(__WXMAC__)
1531 /* values for Mac OS X */
1532 
GetLibraryPath()1533 wxString MP3Exporter::GetLibraryPath()
1534 {
1535    wxString path;
1536 
1537    path = wxT("/Library/Application Support/audacity/libs");
1538    if (wxFileExists(path + wxT("/") + GetLibraryName()))
1539    {
1540         return path;
1541    }
1542 
1543    path = wxT("/usr/local/lib/audacity");
1544    if (wxFileExists(path + wxT("/") + GetLibraryName()))
1545    {
1546         return path;
1547    }
1548 
1549    return wxT("/Library/Application Support/audacity/libs");
1550 }
1551 
GetLibraryName()1552 wxString MP3Exporter::GetLibraryName()
1553 {
1554    if (sizeof(void*) == 8)
1555       return wxT("libmp3lame64bit.dylib");
1556    return wxT("libmp3lame.dylib");
1557 }
1558 
GetLibraryTypes()1559 FileNames::FileTypes MP3Exporter::GetLibraryTypes()
1560 {
1561    return {
1562       (sizeof(void*) == 8)
1563          ? FileNames::FileType{
1564               XO("Only libmp3lame64bit.dylib"), { wxT("libmp3lame64bit.dylib") }
1565            }
1566          : FileNames::FileType{
1567               XO("Only libmp3lame.dylib"), { wxT("libmp3lame.dylib") }
1568            }
1569       ,
1570       FileNames::DynamicLibraries,
1571       FileNames::AllFiles
1572    };
1573 }
1574 
1575 #else //!__WXMAC__
1576 /* Values for Linux / Unix systems */
1577 
GetLibraryPath()1578 wxString MP3Exporter::GetLibraryPath()
1579 {
1580    return wxT(LIBDIR);
1581 }
1582 
GetLibraryName()1583 wxString MP3Exporter::GetLibraryName()
1584 {
1585    return wxT("libmp3lame.so.0");
1586 }
1587 
GetLibraryTypes()1588 FileNames::FileTypes MP3Exporter::GetLibraryTypes()
1589 {
1590    return {
1591       { XO("Only libmp3lame.so.0"), { wxT("libmp3lame.so.0") } },
1592       { XO("Primary shared object files"), { wxT("so") }, true },
1593       { XO("Extended libraries"), { wxT("so*") }, true },
1594       FileNames::AllFiles
1595    };
1596 }
1597 #endif
1598 
1599 #if 0
1600 // Debug routine from BladeMP3EncDLL.c in the libmp3lame distro
1601 static void dump_config( 	lame_global_flags*	gfp )
1602 {
1603    wxPrintf(wxT("\n\nLame_enc configuration options:\n"));
1604    wxPrintf(wxT("==========================================================\n"));
1605 
1606    wxPrintf(wxT("version                =%d\n"),lame_get_version( gfp ) );
1607    wxPrintf(wxT("Layer                  =3\n"));
1608    wxPrintf(wxT("mode                   ="));
1609    switch ( lame_get_mode( gfp ) )
1610    {
1611       case STEREO:       wxPrintf(wxT( "Stereo\n" )); break;
1612       case JOINT_STEREO: wxPrintf(wxT( "Joint-Stereo\n" )); break;
1613       case DUAL_CHANNEL: wxPrintf(wxT( "Forced Stereo\n" )); break;
1614       case MONO:         wxPrintf(wxT( "Mono\n" )); break;
1615       case NOT_SET:      /* FALLTHROUGH */
1616       default:           wxPrintf(wxT( "Error (unknown)\n" )); break;
1617    }
1618 
1619    wxPrintf(wxT("Input sample rate      =%.1f kHz\n"), lame_get_in_samplerate( gfp ) /1000.0 );
1620    wxPrintf(wxT("Output sample rate     =%.1f kHz\n"), lame_get_out_samplerate( gfp ) /1000.0 );
1621 
1622    wxPrintf(wxT("bitrate                =%d kbps\n"), lame_get_brate( gfp ) );
1623    wxPrintf(wxT("Quality Setting        =%d\n"), lame_get_quality( gfp ) );
1624 
1625    wxPrintf(wxT("Low pass frequency     =%d\n"), lame_get_lowpassfreq( gfp ) );
1626    wxPrintf(wxT("Low pass width         =%d\n"), lame_get_lowpasswidth( gfp ) );
1627 
1628    wxPrintf(wxT("High pass frequency    =%d\n"), lame_get_highpassfreq( gfp ) );
1629    wxPrintf(wxT("High pass width        =%d\n"), lame_get_highpasswidth( gfp ) );
1630 
1631    wxPrintf(wxT("No short blocks        =%d\n"), lame_get_no_short_blocks( gfp ) );
1632    wxPrintf(wxT("Force short blocks     =%d\n"), lame_get_force_short_blocks( gfp ) );
1633 
1634    wxPrintf(wxT("de-emphasis            =%d\n"), lame_get_emphasis( gfp ) );
1635    wxPrintf(wxT("private flag           =%d\n"), lame_get_extension( gfp ) );
1636 
1637    wxPrintf(wxT("copyright flag         =%d\n"), lame_get_copyright( gfp ) );
1638    wxPrintf(wxT("original flag          =%d\n"),	lame_get_original( gfp ) );
1639    wxPrintf(wxT("CRC                    =%s\n"), lame_get_error_protection( gfp ) ? wxT("on") : wxT("off") );
1640    wxPrintf(wxT("Fast mode              =%s\n"), ( lame_get_quality( gfp ) )? wxT("enabled") : wxT("disabled") );
1641    wxPrintf(wxT("Force mid/side stereo  =%s\n"), ( lame_get_force_ms( gfp ) )?wxT("enabled"):wxT("disabled") );
1642    wxPrintf(wxT("Padding Type           =%d\n"), (int) lame_get_padding_type( gfp ) );
1643    wxPrintf(wxT("Disable Reservoir      =%d\n"), lame_get_disable_reservoir( gfp ) );
1644    wxPrintf(wxT("Allow diff-short       =%d\n"), lame_get_allow_diff_short( gfp ) );
1645    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
1646    wxPrintf(wxT("Strict ISO Encoding    =%s\n"), ( lame_get_strict_ISO( gfp ) ) ?wxT("Yes"):wxT("No"));
1647    wxPrintf(wxT("Scale                  =%5.2f\n"), lame_get_scale( gfp ) );
1648 
1649    wxPrintf(wxT("VBR                    =%s, VBR_q =%d, VBR method ="),
1650             ( lame_get_VBR( gfp ) !=vbr_off ) ? wxT("enabled"): wxT("disabled"),
1651             lame_get_VBR_q( gfp ) );
1652 
1653    switch ( lame_get_VBR( gfp ) )
1654    {
1655       case vbr_off:	wxPrintf(wxT( "vbr_off\n" ));	break;
1656       case vbr_mt :	wxPrintf(wxT( "vbr_mt \n" ));	break;
1657       case vbr_rh :	wxPrintf(wxT( "vbr_rh \n" ));	break;
1658       case vbr_mtrh:	wxPrintf(wxT( "vbr_mtrh \n" ));	break;
1659       case vbr_abr:
1660          wxPrintf(wxT( "vbr_abr (average bitrate %d kbps)\n"), lame_get_VBR_mean_bitrate_kbps( gfp ) );
1661          break;
1662       default:
1663          wxPrintf(wxT("error, unknown VBR setting\n"));
1664          break;
1665    }
1666 
1667    wxPrintf(wxT("Vbr Min bitrate        =%d kbps\n"), lame_get_VBR_min_bitrate_kbps( gfp ) );
1668    wxPrintf(wxT("Vbr Max bitrate        =%d kbps\n"), lame_get_VBR_max_bitrate_kbps( gfp ) );
1669 
1670    wxPrintf(wxT("Write VBR Header       =%s\n"), ( lame_get_bWriteVbrTag( gfp ) ) ?wxT("Yes"):wxT("No"));
1671    wxPrintf(wxT("VBR Hard min           =%d\n"), lame_get_VBR_hard_min( gfp ) );
1672 
1673    wxPrintf(wxT("ATH Only               =%d\n"), lame_get_ATHonly( gfp ) );
1674    wxPrintf(wxT("ATH short              =%d\n"), lame_get_ATHshort( gfp ) );
1675    wxPrintf(wxT("ATH no                 =%d\n"), lame_get_noATH( gfp ) );
1676    wxPrintf(wxT("ATH type               =%d\n"), lame_get_ATHtype( gfp ) );
1677    wxPrintf(wxT("ATH lower              =%f\n"), lame_get_ATHlower( gfp ) );
1678    wxPrintf(wxT("ATH aa                 =%d\n"), lame_get_athaa_type( gfp ) );
1679    wxPrintf(wxT("ATH aa  loudapprox     =%d\n"), lame_get_athaa_loudapprox( gfp ) );
1680    wxPrintf(wxT("ATH aa  sensitivity    =%f\n"), lame_get_athaa_sensitivity( gfp ) );
1681 
1682    wxPrintf(wxT("Experimental nspsytune =%d\n"), lame_get_exp_nspsytune( gfp ) );
1683    wxPrintf(wxT("Experimental X         =%d\n"), lame_get_experimentalX( gfp ) );
1684    wxPrintf(wxT("Experimental Y         =%d\n"), lame_get_experimentalY( gfp ) );
1685    wxPrintf(wxT("Experimental Z         =%d\n"), lame_get_experimentalZ( gfp ) );
1686 }
1687 #endif
1688 
1689 //----------------------------------------------------------------------------
1690 // ExportMP3
1691 //----------------------------------------------------------------------------
1692 
1693 class ExportMP3 final : public ExportPlugin
1694 {
1695 public:
1696 
1697    ExportMP3();
1698    bool CheckFileName(wxFileName & filename, int format) override;
1699 
1700    // Required
1701 
1702    void OptionsCreate(ShuttleGui &S, int format) override;
1703    ProgressResult Export(AudacityProject *project,
1704                std::unique_ptr<ProgressDialog> &pDialog,
1705                unsigned channels,
1706                const wxFileNameWrapper &fName,
1707                bool selectedOnly,
1708                double t0,
1709                double t1,
1710                MixerSpec *mixerSpec = NULL,
1711                const Tags *metadata = NULL,
1712                int subformat = 0) override;
1713 
1714 private:
1715 
1716    int AskResample(int bitrate, int rate, int lowrate, int highrate);
1717    unsigned long AddTags(AudacityProject *project, ArrayOf<char> &buffer, bool *endOfFile, const Tags *tags);
1718 #ifdef USE_LIBID3TAG
1719    void AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name);
1720 #endif
1721    int SetNumExportChannels() override;
1722 };
1723 
ExportMP3()1724 ExportMP3::ExportMP3()
1725 :  ExportPlugin()
1726 {
1727    AddFormat();
1728    SetFormat(wxT("MP3"),0);
1729    AddExtension(wxT("mp3"),0);
1730    SetMaxChannels(2,0);
1731    SetCanMetaData(true,0);
1732    SetDescription(XO("MP3 Files"),0);
1733 }
1734 
CheckFileName(wxFileName & WXUNUSED (filename),int WXUNUSED (format))1735 bool ExportMP3::CheckFileName(wxFileName & WXUNUSED(filename), int WXUNUSED(format))
1736 {
1737 #ifndef DISABLE_DYNAMIC_LOADING_LAME
1738    MP3Exporter exporter;
1739 
1740    if (!exporter.LoadLibrary(wxTheApp->GetTopWindow(), MP3Exporter::Maybe)) {
1741       AudacityMessageBox( XO("Could not open MP3 encoding library!") );
1742       gPrefs->Write(wxT("/MP3/MP3LibPath"), wxString(wxT("")));
1743       gPrefs->Flush();
1744 
1745       return false;
1746    }
1747 #endif // DISABLE_DYNAMIC_LOADING_LAME
1748 
1749    return true;
1750 }
1751 
SetNumExportChannels()1752 int ExportMP3::SetNumExportChannels()
1753 {
1754    bool mono;
1755    gPrefs->Read(wxT("/FileFormats/MP3ForceMono"), &mono, 0);
1756 
1757    return (mono)? 1 : -1;
1758 }
1759 
1760 
Export(AudacityProject * project,std::unique_ptr<ProgressDialog> & pDialog,unsigned channels,const wxFileNameWrapper & fName,bool selectionOnly,double t0,double t1,MixerSpec * mixerSpec,const Tags * metadata,int WXUNUSED (subformat))1761 ProgressResult ExportMP3::Export(AudacityProject *project,
1762                        std::unique_ptr<ProgressDialog> &pDialog,
1763                        unsigned channels,
1764                        const wxFileNameWrapper &fName,
1765                        bool selectionOnly,
1766                        double t0,
1767                        double t1,
1768                        MixerSpec *mixerSpec,
1769                        const Tags *metadata,
1770                        int WXUNUSED(subformat))
1771 {
1772    int rate = lrint( ProjectRate::Get( *project ).GetRate());
1773 #ifndef DISABLE_DYNAMIC_LOADING_LAME
1774    wxWindow *parent = ProjectWindow::Find( project );
1775 #endif // DISABLE_DYNAMIC_LOADING_LAME
1776    const auto &tracks = TrackList::Get( *project );
1777    MP3Exporter exporter;
1778 
1779 #ifdef DISABLE_DYNAMIC_LOADING_LAME
1780    if (!exporter.InitLibrary(wxT(""))) {
1781       AudacityMessageBox( XO("Could not initialize MP3 encoding library!") );
1782       gPrefs->Write(wxT("/MP3/MP3LibPath"), wxString(wxT("")));
1783       gPrefs->Flush();
1784 
1785       return ProgressResult::Cancelled;
1786    }
1787 #else
1788    if (!exporter.LoadLibrary(parent, MP3Exporter::Maybe)) {
1789       AudacityMessageBox( XO("Could not open MP3 encoding library!") );
1790       gPrefs->Write(wxT("/MP3/MP3LibPath"), wxString(wxT("")));
1791       gPrefs->Flush();
1792 
1793       return ProgressResult::Cancelled;
1794    }
1795 
1796    if (!exporter.ValidLibraryLoaded()) {
1797       AudacityMessageBox( XO("Not a valid or supported MP3 encoding library!") );
1798       gPrefs->Write(wxT("/MP3/MP3LibPath"), wxString(wxT("")));
1799       gPrefs->Flush();
1800 
1801       return ProgressResult::Cancelled;
1802    }
1803 #endif // DISABLE_DYNAMIC_LOADING_LAME
1804 
1805    // Retrieve preferences
1806    int highrate = 48000;
1807    int lowrate = 8000;
1808    int bitrate = 0;
1809    int brate;
1810    //int vmode;
1811    bool forceMono;
1812 
1813    gPrefs->Read(wxT("/FileFormats/MP3Bitrate"), &brate, 128);
1814    auto rmode = MP3RateModeSetting.ReadEnumWithDefault( MODE_CBR );
1815    //gPrefs->Read(wxT("/FileFormats/MP3VarMode"), &vmode, ROUTINE_FAST);
1816    auto cmode = MP3ChannelModeSetting.ReadEnumWithDefault( CHANNEL_STEREO );
1817    gPrefs->Read(wxT("/FileFormats/MP3ForceMono"), &forceMono, 0);
1818 
1819    // Set the bitrate/quality and mode
1820    if (rmode == MODE_SET) {
1821       brate = ValidateValue(setRateNames.size(), brate, PRESET_STANDARD);
1822       //int r = ValidateValue( varModeNames.size(), vmode, ROUTINE_FAST );
1823       exporter.SetMode(MODE_SET);
1824       exporter.SetQuality(brate/*, r*/);
1825    }
1826    else if (rmode == MODE_VBR) {
1827       brate = ValidateValue( varRateNames.size(), brate, QUALITY_2 );
1828       //int r = ValidateValue( varModeNames.size(), vmode, ROUTINE_FAST );
1829       exporter.SetMode(MODE_VBR);
1830       exporter.SetQuality(brate/*, r*/);
1831    }
1832    else if (rmode == MODE_ABR) {
1833       brate = ValidateIndex( fixRateValues, brate, 6 /* 128 kbps */ );
1834       bitrate = fixRateValues[ brate ];
1835       exporter.SetMode(MODE_ABR);
1836       exporter.SetBitrate(bitrate);
1837 
1838       if (bitrate > 160) {
1839          lowrate = 32000;
1840       }
1841       else if (bitrate < 32 || bitrate == 144) {
1842          highrate = 24000;
1843       }
1844    }
1845    else {
1846       brate = ValidateIndex( fixRateValues, brate, 6 /* 128 kbps */ );
1847       bitrate = fixRateValues[ brate ];
1848       exporter.SetMode(MODE_CBR);
1849       exporter.SetBitrate(bitrate);
1850 
1851       if (bitrate > 160) {
1852          lowrate = 32000;
1853       }
1854       else if (bitrate < 32 || bitrate == 144) {
1855          highrate = 24000;
1856       }
1857    }
1858 
1859    // Verify sample rate
1860    if (!make_iterator_range( sampRates ).contains( rate ) ||
1861       (rate < lowrate) || (rate > highrate)) {
1862       rate = AskResample(bitrate, rate, lowrate, highrate);
1863       if (rate == 0) {
1864          return ProgressResult::Cancelled;
1865       }
1866    }
1867 
1868    // Set the channel mode
1869    if (forceMono) {
1870       exporter.SetChannel(CHANNEL_MONO);
1871    }
1872    else if (cmode == CHANNEL_JOINT) {
1873       exporter.SetChannel(CHANNEL_JOINT);
1874    }
1875    else {
1876       exporter.SetChannel(CHANNEL_STEREO);
1877    }
1878 
1879    auto inSamples = exporter.InitializeStream(channels, rate);
1880    if (((int)inSamples) < 0) {
1881       AudacityMessageBox( XO("Unable to initialize MP3 stream") );
1882       return ProgressResult::Cancelled;
1883    }
1884 
1885    // Put ID3 tags at beginning of file
1886    if (metadata == NULL)
1887       metadata = &Tags::Get( *project );
1888 
1889    // Open file for writing
1890    wxFFile outFile(fName.GetFullPath(), wxT("w+b"));
1891    if (!outFile.IsOpened()) {
1892       AudacityMessageBox( XO("Unable to open target file for writing") );
1893       return ProgressResult::Cancelled;
1894    }
1895 
1896    ArrayOf<char> id3buffer;
1897    bool endOfFile;
1898    unsigned long id3len = AddTags(project, id3buffer, &endOfFile, metadata);
1899    if (id3len && !endOfFile) {
1900       if (id3len > outFile.Write(id3buffer.get(), id3len)) {
1901          // TODO: more precise message
1902          ShowExportErrorDialog("MP3:1882");
1903          return ProgressResult::Cancelled;
1904       }
1905    }
1906 
1907    wxFileOffset pos = outFile.Tell();
1908    auto updateResult = ProgressResult::Success;
1909    int bytes = 0;
1910 
1911    size_t bufferSize = std::max(0, exporter.GetOutBufferSize());
1912    if (bufferSize <= 0) {
1913       // TODO: more precise message
1914       ShowExportErrorDialog("MP3:1849");
1915       return ProgressResult::Cancelled;
1916    }
1917 
1918    ArrayOf<unsigned char> buffer{ bufferSize };
1919    wxASSERT(buffer);
1920 
1921    {
1922       auto mixer = CreateMixer(tracks, selectionOnly,
1923          t0, t1,
1924          channels, inSamples, true,
1925          rate, floatSample, mixerSpec);
1926 
1927       TranslatableString title;
1928       if (rmode == MODE_SET) {
1929          title = (selectionOnly ?
1930             XO("Exporting selected audio with %s preset") :
1931             XO("Exporting the audio with %s preset"))
1932                .Format( setRateNamesShort[brate] );
1933       }
1934       else if (rmode == MODE_VBR) {
1935          title = (selectionOnly ?
1936             XO("Exporting selected audio with VBR quality %s") :
1937             XO("Exporting the audio with VBR quality %s"))
1938                .Format( varRateNames[brate] );
1939       }
1940       else {
1941          title = (selectionOnly ?
1942             XO("Exporting selected audio at %d Kbps") :
1943             XO("Exporting the audio at %d Kbps"))
1944                .Format( bitrate );
1945       }
1946 
1947       InitProgress( pDialog, fName, title );
1948       auto &progress = *pDialog;
1949 
1950       while (updateResult == ProgressResult::Success) {
1951          auto blockLen = mixer->Process(inSamples);
1952 
1953          if (blockLen == 0) {
1954             break;
1955          }
1956 
1957          float *mixed = (float *)mixer->GetBuffer();
1958 
1959          if ((int)blockLen < inSamples) {
1960             if (channels > 1) {
1961                bytes = exporter.EncodeRemainder(mixed, blockLen, buffer.get());
1962             }
1963             else {
1964                bytes = exporter.EncodeRemainderMono(mixed, blockLen, buffer.get());
1965             }
1966          }
1967          else {
1968             if (channels > 1) {
1969                bytes = exporter.EncodeBuffer(mixed, buffer.get());
1970             }
1971             else {
1972                bytes = exporter.EncodeBufferMono(mixed, buffer.get());
1973             }
1974          }
1975 
1976          if (bytes < 0) {
1977             auto msg = XO("Error %ld returned from MP3 encoder")
1978                .Format( bytes );
1979             AudacityMessageBox( msg );
1980             updateResult = ProgressResult::Cancelled;
1981             break;
1982          }
1983 
1984          if (bytes > (int)outFile.Write(buffer.get(), bytes)) {
1985             // TODO: more precise message
1986             ShowDiskFullExportErrorDialog(fName);
1987             updateResult = ProgressResult::Cancelled;
1988             break;
1989          }
1990 
1991          updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
1992       }
1993    }
1994 
1995    if ( updateResult == ProgressResult::Success ||
1996         updateResult == ProgressResult::Stopped ) {
1997       bytes = exporter.FinishStream(buffer.get());
1998 
1999       if (bytes < 0) {
2000          // TODO: more precise message
2001          ShowExportErrorDialog("MP3:1981");
2002          return ProgressResult::Cancelled;
2003       }
2004 
2005       if (bytes > 0) {
2006          if (bytes > (int)outFile.Write(buffer.get(), bytes)) {
2007             // TODO: more precise message
2008             ShowExportErrorDialog("MP3:1988");
2009             return ProgressResult::Cancelled;
2010          }
2011       }
2012 
2013       // Write ID3 tag if it was supposed to be at the end of the file
2014       if (id3len > 0 && endOfFile) {
2015          if (bytes > (int)outFile.Write(id3buffer.get(), id3len)) {
2016             // TODO: more precise message
2017             ShowExportErrorDialog("MP3:1997");
2018             return ProgressResult::Cancelled;
2019          }
2020       }
2021 
2022       // Always write the info (Xing/Lame) tag.  Until we stop supporting Lame
2023       // versions before 3.98, we must do this after the MP3 file has been
2024       // closed.
2025       //
2026       // Also, if beWriteInfoTag() is used, mGF will no longer be valid after
2027       // this call, so do not use it.
2028       if (!exporter.PutInfoTag(outFile, pos) ||
2029           !outFile.Flush() ||
2030           !outFile.Close()) {
2031          // TODO: more precise message
2032          ShowExportErrorDialog("MP3:2012");
2033          return ProgressResult::Cancelled;
2034       }
2035    }
2036 
2037    return updateResult;
2038 }
2039 
OptionsCreate(ShuttleGui & S,int format)2040 void ExportMP3::OptionsCreate(ShuttleGui &S, int format)
2041 {
2042    S.AddWindow( safenew ExportMP3Options{ S.GetParent(), format } );
2043 }
2044 
AskResample(int bitrate,int rate,int lowrate,int highrate)2045 int ExportMP3::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;
2050    ShuttleGui S(&d, eIsCreating);
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
2113 struct id3_tag_deleter {
operator ()id3_tag_deleter2114    void operator () (id3_tag *p) const { if (p) id3_tag_delete(p); }
2115 };
2116 using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
2117 #endif
2118 
2119 // returns buffer len; caller frees
AddTags(AudacityProject * WXUNUSED (project),ArrayOf<char> & buffer,bool * endOfFile,const Tags * tags)2120 unsigned long ExportMP3::AddTags(AudacityProject *WXUNUSED(project), 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
AddFrame(struct id3_tag * tp,const wxString & n,const wxString & v,const char * name)2182 void ExportMP3::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 
2193    MallocString<id3_ucs4_t> ucs4{
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 
2226 static Exporter::RegisteredExportPlugin sRegisteredPlugin{ "MP3",
__anon188ab29d0502null2227    []{ return std::make_unique< ExportMP3 >(); }
2228 };
2229 
2230 //----------------------------------------------------------------------------
2231 // Return library version
2232 //----------------------------------------------------------------------------
2233 
GetMP3Version(wxWindow * parent,bool prompt)2234 TranslatableString 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 
2258