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