Audacity 3.2.0
ExportMP2.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ExportMP2.cpp
6
7 Joshua Haberman
8 Markus Meyer
9
10 Copyright 2002, 2003 Joshua Haberman.
11 Copyright 2006 Markus Meyer
12 Some portions may be Copyright 2003 Paolo Patruno.
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
28*******************************************************************/
37#ifdef USE_LIBTWOLAME
38
39#include <wx/defs.h>
40#include <wx/textctrl.h>
41#include <wx/dynlib.h>
42#include <wx/window.h>
43#include <wx/log.h>
44#include <wx/stream.h>
45
46#include "Export.h"
47#include "FileIO.h"
48#include "Mix.h"
49#include "Prefs.h"
50#include "ProjectRate.h"
51#include "ShuttleGui.h"
52#include "Tags.h"
53#include "Track.h"
54#include "AudacityMessageBox.h"
55#include "ProgressDialog.h"
56
57#define LIBTWOLAME_STATIC
58#include "twolame.h"
59
60#ifdef USE_LIBID3TAG
61 #include <id3tag.h>
62 // DM: the following functions were supposed to have been
63 // included in id3tag.h - should be fixed in the next release
64 // of mad.
65 extern "C" {
66 struct id3_frame *id3_frame_new(char const *);
67 id3_length_t id3_latin1_length(id3_latin1_t const *);
68 void id3_latin1_decode(id3_latin1_t const *, id3_ucs4_t *);
69 }
70#endif
71
72//----------------------------------------------------------------------------
73// ExportMP2Options
74//----------------------------------------------------------------------------
75
76namespace {
77
78// i18n-hint kbps abbreviates "thousands of bits per second"
79inline TranslatableString n_kbps( int n ) { return XO("%d kbps").Format( n ); }
80
82 n_kbps(16),
83 n_kbps(24),
84 n_kbps(32),
85 n_kbps(40),
86 n_kbps(48),
87 n_kbps(56),
88 n_kbps(64),
89 n_kbps(80),
90 n_kbps(96),
91 n_kbps(112),
92 n_kbps(128),
93 n_kbps(160),
94 n_kbps(192),
95 n_kbps(224),
96 n_kbps(256),
97 n_kbps(320),
98 n_kbps(384),
99};
100
101const std::vector< int > BitRateValues {
102 16,
103 24,
104 32,
105 40,
106 48,
107 56,
108 64,
109 80,
110 96,
111 112,
112 128,
113 160,
114 192,
115 224,
116 256,
117 320,
118 384,
119};
120
121}
122
123class ExportMP2Options final : public wxPanelWrapper
124{
125public:
126 ExportMP2Options(wxWindow *parent, int format);
127 virtual ~ExportMP2Options();
128
129 void PopulateOrExchange(ShuttleGui & S);
130 bool TransferDataToWindow() override;
131 bool TransferDataFromWindow() override;
132};
133
136ExportMP2Options::ExportMP2Options(wxWindow *parent, int WXUNUSED(format))
137: wxPanelWrapper(parent, wxID_ANY)
138{
140 PopulateOrExchange(S);
141
142 TransferDataToWindow();
143}
144
147ExportMP2Options::~ExportMP2Options()
148{
149 TransferDataFromWindow();
150}
151
154void ExportMP2Options::PopulateOrExchange(ShuttleGui & S)
155{
156 IntSetting Setting{ L"/FileFormats/MP2Bitrate", 160 };
157 S.StartVerticalLay();
158 {
159 S.StartHorizontalLay(wxCENTER);
160 {
161 S.StartMultiColumn(2, wxCENTER);
162 {
163 S.TieNumberAsChoice(XXO("Bit Rate:"), Setting,
165 }
166 S.EndMultiColumn();
167 }
168 S.EndHorizontalLay();
169 }
170 S.EndVerticalLay();
171}
172
175bool ExportMP2Options::TransferDataToWindow()
176{
177 return true;
178}
179
182bool ExportMP2Options::TransferDataFromWindow()
183{
185 PopulateOrExchange(S);
186
187 gPrefs->Flush();
188
189 return true;
190}
191
192//----------------------------------------------------------------------------
193// ExportMP2
194//----------------------------------------------------------------------------
195
196class ExportMP2 final : public ExportPlugin
197{
198public:
199
200 ExportMP2();
201
202 // Required
203
204 void OptionsCreate(ShuttleGui &S, int format) override;
206 std::unique_ptr<BasicUI::ProgressDialog> &pDialog,
207 unsigned channels,
208 const wxFileNameWrapper &fName,
209 bool selectedOnly,
210 double t0,
211 double t1,
212 MixerSpec *mixerSpec = NULL,
213 const Tags *metadata = NULL,
214 int subformat = 0) override;
215
216private:
217
218 int AddTags(AudacityProject *project, ArrayOf<char> &buffer, bool *endOfFile, const Tags *tags);
219#ifdef USE_LIBID3TAG
220 void AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name);
221#endif
222
223};
224
225ExportMP2::ExportMP2()
226: ExportPlugin()
227{
228 AddFormat();
229 SetFormat(wxT("MP2"),0);
230 AddExtension(wxT("mp2"),0);
231 SetMaxChannels(2,0);
232 SetCanMetaData(true,0);
233 SetDescription(XO("MP2 Files"),0);
234}
235
236ProgressResult ExportMP2::Export(AudacityProject *project,
237 std::unique_ptr<BasicUI::ProgressDialog> &pDialog,
238 unsigned channels, const wxFileNameWrapper &fName,
239 bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata,
240 int WXUNUSED(subformat))
241{
242 bool stereo = (channels == 2);
243 long bitrate = gPrefs->Read(wxT("/FileFormats/MP2Bitrate"), 160);
244 double rate = ProjectRate::Get(*project).GetRate();
245 const auto &tracks = TrackList::Get( *project );
246
247 wxLogNull logNo; /* temporarily disable wxWidgets error messages */
248
249 twolame_options *encodeOptions;
250 encodeOptions = twolame_init();
251 auto cleanup = finally( [&] { twolame_close(&encodeOptions); } );
252
253 twolame_set_in_samplerate(encodeOptions, (int)(rate + 0.5));
254 twolame_set_out_samplerate(encodeOptions, (int)(rate + 0.5));
255 twolame_set_bitrate(encodeOptions, bitrate);
256 twolame_set_num_channels(encodeOptions, stereo ? 2 : 1);
257
258 if (twolame_init_params(encodeOptions) != 0)
259 {
261 XO("Cannot export MP2 with this sample rate and bit rate"),
262 XO("Error"),
263 wxICON_STOP);
265 }
266
267 // Put ID3 tags at beginning of file
268 if (metadata == NULL)
269 metadata = &Tags::Get( *project );
270
271 FileIO outFile(fName, FileIO::Output);
272 if (!outFile.IsOpened()) {
273 AudacityMessageBox( XO("Unable to open target file for writing") );
275 }
276
277 ArrayOf<char> id3buffer;
278 int id3len;
279 bool endOfFile;
280 id3len = AddTags(project, id3buffer, &endOfFile, metadata);
281 if (id3len && !endOfFile) {
282 if ( outFile.Write(id3buffer.get(), id3len).GetLastError() ) {
283 // TODO: more precise message
284 ShowExportErrorDialog("MP2:292");
286 }
287 }
288
289 // Values taken from the twolame simple encoder sample
290 const size_t pcmBufferSize = 9216 / 2; // number of samples
291 const size_t mp2BufferSize = 16384u; // bytes
292
293 // We allocate a buffer which is twice as big as the
294 // input buffer, which should always be enough.
295 // We have to multiply by 4 because one sample is 2 bytes wide!
296 ArrayOf<unsigned char> mp2Buffer{ mp2BufferSize };
297
298 auto updateResult = ProgressResult::Success;
299 {
300 auto mixer = CreateMixer(tracks, selectionOnly,
301 t0, t1,
302 stereo ? 2 : 1, pcmBufferSize, true,
303 rate, int16Sample, mixerSpec);
304
305 InitProgress( pDialog, fName,
306 selectionOnly
307 ? XO("Exporting selected audio at %ld kbps")
308 .Format( bitrate )
309 : XO("Exporting the audio at %ld kbps")
310 .Format( bitrate ) );
311 auto &progress = *pDialog;
312
313 while (updateResult == ProgressResult::Success) {
314 auto pcmNumSamples = mixer->Process();
315 if (pcmNumSamples == 0)
316 break;
317
318 short *pcmBuffer = (short *)mixer->GetBuffer();
319
320 int mp2BufferNumBytes = twolame_encode_buffer_interleaved(
321 encodeOptions,
322 pcmBuffer,
323 pcmNumSamples,
324 mp2Buffer.get(),
325 mp2BufferSize);
326
327 if (mp2BufferNumBytes < 0) {
328 // TODO: more precise message
329 ShowExportErrorDialog("MP2:339");
330 updateResult = ProgressResult::Cancelled;
331 break;
332 }
333
334 if ( outFile.Write(mp2Buffer.get(), mp2BufferNumBytes).GetLastError() ) {
335 // TODO: more precise message
338 }
339
340 updateResult = progress.Poll(mixer->MixGetCurrentTime() - t0, t1 - t0);
341 }
342 }
343
344 int mp2BufferNumBytes = twolame_encode_flush(
345 encodeOptions,
346 mp2Buffer.get(),
347 mp2BufferSize);
348
349 if (mp2BufferNumBytes > 0)
350 if ( outFile.Write(mp2Buffer.get(), mp2BufferNumBytes).GetLastError() ) {
351 // TODO: more precise message
352 ShowExportErrorDialog("MP2:362");
354 }
355
356 /* Write ID3 tag if it was supposed to be at the end of the file */
357
358 if (id3len && endOfFile)
359 if ( outFile.Write(id3buffer.get(), id3len).GetLastError() ) {
360 // TODO: more precise message
361 ShowExportErrorDialog("MP2:371");
363 }
364
365 if ( !outFile.Close() ) {
366 // TODO: more precise message
367 ShowExportErrorDialog("MP2:377");
369 }
370
371 return updateResult;
372}
373
374void ExportMP2::OptionsCreate(ShuttleGui &S, int format)
375{
376 S.AddWindow( safenew ExportMP2Options{ S.GetParent(), format } );
377}
378
379
380#ifdef USE_LIBID3TAG
381struct id3_tag_deleter {
382 void operator () (id3_tag *p) const { if (p) id3_tag_delete(p); }
383};
384using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
385#endif
386
387// returns buffer len; caller frees
388int ExportMP2::AddTags(
389 AudacityProject * WXUNUSED(project), ArrayOf< char > &buffer,
390 bool *endOfFile, const Tags *tags)
391{
392#ifdef USE_LIBID3TAG
393 id3_tag_holder tp { id3_tag_new() };
394
395 for (const auto &pair : tags->GetRange()) {
396 const auto &n = pair.first;
397 const auto &v = pair.second;
398 const char *name = "TXXX";
399
400 if (n.CmpNoCase(TAG_TITLE) == 0) {
401 name = ID3_FRAME_TITLE;
402 }
403 else if (n.CmpNoCase(TAG_ARTIST) == 0) {
404 name = ID3_FRAME_ARTIST;
405 }
406 else if (n.CmpNoCase(TAG_ALBUM) == 0) {
407 name = ID3_FRAME_ALBUM;
408 }
409 else if (n.CmpNoCase(TAG_YEAR) == 0) {
410 // LLL: Some apps do not like the newer frame ID (ID3_FRAME_YEAR),
411 // so we add old one as well.
412 AddFrame(tp.get(), n, v, "TYER");
413 name = ID3_FRAME_YEAR;
414 }
415 else if (n.CmpNoCase(TAG_GENRE) == 0) {
416 name = ID3_FRAME_GENRE;
417 }
418 else if (n.CmpNoCase(TAG_COMMENTS) == 0) {
419 name = ID3_FRAME_COMMENT;
420 }
421 else if (n.CmpNoCase(TAG_TRACK) == 0) {
422 name = ID3_FRAME_TRACK;
423 }
424
425 AddFrame(tp.get(), n, v, name);
426 }
427
428 tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression
429
430 // If this version of libid3tag supports it, use v2.3 ID3
431 // tags instead of the newer, but less well supported, v2.4
432 // that libid3tag uses by default.
433 #ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
434 tp->options |= ID3_TAG_OPTION_ID3V2_3;
435 #endif
436
437 *endOfFile = false;
438
439 id3_length_t len;
440
441 len = id3_tag_render(tp.get(), 0);
442 buffer.reinit(len);
443 len = id3_tag_render(tp.get(), (id3_byte_t *)buffer.get());
444
445
446 return len;
447#else //ifdef USE_LIBID3TAG
448 return 0;
449#endif
450}
451
452#ifdef USE_LIBID3TAG
453void ExportMP2::AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name)
454{
455 struct id3_frame *frame = id3_frame_new(name);
456
457 if (!n.IsAscii() || !v.IsAscii()) {
458 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
459 }
460 else {
461 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
462 }
463
465 id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) v.mb_str(wxConvUTF8)) };
466
467 if (strcmp(name, ID3_FRAME_COMMENT) == 0) {
468 // A hack to get around iTunes not recognizing the comment. The
469 // language defaults to XXX and, since it's not a valid language,
470 // iTunes just ignores the tag. So, either set it to a valid language
471 // (which one???) or just clear it. Unfortunately, there's no supported
472 // way of clearing the field, so do it directly.
473 id3_field *f = id3_frame_field(frame, 1);
474 memset(f->immediate.value, 0, sizeof(f->immediate.value));
475 id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
476 }
477 else if (strcmp(name, "TXXX") == 0) {
478 id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
479
480 ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) n.mb_str(wxConvUTF8)));
481
482 id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
483 }
484 else {
485 auto addr = ucs4.get();
486 id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
487 }
488
489 id3_tag_attachframe(tp, frame);
490}
491#endif
492
494 []{ return std::make_unique< ExportMP2 >(); }
495};
496
497#endif // #ifdef USE_LIBTWOLAME
498
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
const TranslatableString name
Definition: Distortion.cpp:76
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption, bool allowReporting)
Definition: Export.cpp:1503
void ShowDiskFullExportErrorDialog(const wxFileNameWrapper &fileName)
Definition: Export.cpp:1516
static Exporter::RegisteredExportPlugin sRegisteredPlugin
Definition: ExportCL.cpp:796
TranslatableString n_kbps(int n)
Definition: ExportMP3.cpp:127
int format
Definition: ExportPCM.cpp:53
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:146
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:70
an object holding per-project preferred sample rate
constexpr sampleFormat int16Sample
Definition: SampleFormat.h:41
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:46
@ eIsSavingToPrefs
Definition: ShuttleGui.h:47
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
#define S(N)
Definition: ToChars.cpp:64
declares abstract base class Track, TrackList, and iterators over TrackList
std::vector< TranslatableString > TranslatableStrings
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:57
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
virtual ProgressResult Export(AudacityProject *project, std::unique_ptr< BasicUI::ProgressDialog > &pDialog, unsigned channels, const wxFileNameWrapper &fName, bool selectedOnly, double t0, double t1, MixerSpec *mixerSpec=NULL, const Tags *metadata=NULL, int subformat=0)=0
called to export audio into a file.
virtual void OptionsCreate(ShuttleGui &S, int format)=0
Definition: Export.cpp:208
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
Definition: FileIO.h:22
@ Output
Definition: FileIO.h:27
Abstract base class used in importing a file.
Specialization of Setting for int.
Definition: Prefs.h:349
A matrix of booleans, one row per input channel, column per output.
Definition: MixerOptions.h:32
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
Definition: Prefs.h:173
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
ID3 Tags (for MP3)
Definition: Tags.h:73
Iterators GetRange() const
Definition: Tags.cpp:436
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:385
Holds a msgid for the translation catalog; may also bind format arguments.
ProgressResult
Definition: BasicUI.h:147
const std::vector< int > BitRateValues
const TranslatableStrings BitRateNames