Audacity  2.2.2
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 *******************************************************************/
35 #include "../Audacity.h"
36 #include "ExportMP2.h"
37 
38 #ifdef USE_LIBTWOLAME
39 
40 #include <wx/defs.h>
41 #include <wx/textctrl.h>
42 #include <wx/dynlib.h>
43 #include <wx/utils.h>
44 #include <wx/timer.h>
45 #include <wx/window.h>
46 #include <wx/log.h>
47 #include <wx/intl.h>
48 
49 #include "Export.h"
50 #include "../FileIO.h"
51 #include "../Internat.h"
52 #include "../Mix.h"
53 #include "../Prefs.h"
54 #include "../Project.h"
55 #include "../ShuttleGui.h"
56 #include "../Tags.h"
57 #include "../Track.h"
58 #include "../widgets/ErrorDialog.h"
59 
60 #define LIBTWOLAME_STATIC
61 #include "twolame.h"
62 
63 #ifdef USE_LIBID3TAG
64  #include <id3tag.h>
65  // DM: the following functions were supposed to have been
66  // included in id3tag.h - should be fixed in the next release
67  // of mad.
68  extern "C" {
69  struct id3_frame *id3_frame_new(char const *);
70  id3_length_t id3_latin1_length(id3_latin1_t const *);
71  void id3_latin1_decode(id3_latin1_t const *, id3_ucs4_t *);
72  }
73 #endif
74 
75 //----------------------------------------------------------------------------
76 // ExportMP2Options
77 //----------------------------------------------------------------------------
78 
79 static int iBitrates[] = {
80  16, 24, 32, 40, 48, 56, 64,
81  80, 96, 112, 128, 160,
82  192, 224, 256, 320, 384
83 };
84 
85 class ExportMP2Options final : public wxPanelWrapper
86 {
87 public:
88  ExportMP2Options(wxWindow *parent, int format);
89  virtual ~ExportMP2Options();
90 
91  void PopulateOrExchange(ShuttleGui & S);
92  bool TransferDataToWindow() override;
93  bool TransferDataFromWindow() override;
94 
95 private:
96  wxArrayString mBitRateNames;
97  std::vector<int> mBitRateLabels;
98 };
99 
102 ExportMP2Options::ExportMP2Options(wxWindow *parent, int WXUNUSED(format))
103 : wxPanelWrapper(parent, wxID_ANY)
104 {
105  for (unsigned int i=0; i < (sizeof(iBitrates)/sizeof(int)); i++)
106  {
107  mBitRateNames.Add(wxString::Format(_("%i kbps"),iBitrates[i]));
108  mBitRateLabels.push_back(iBitrates[i]);
109  }
110 
112  PopulateOrExchange(S);
113 
114  TransferDataToWindow();
115 }
116 
119 ExportMP2Options::~ExportMP2Options()
120 {
121  TransferDataFromWindow();
122 }
123 
126 void ExportMP2Options::PopulateOrExchange(ShuttleGui & S)
127 {
128  S.StartVerticalLay();
129  {
130  S.StartHorizontalLay(wxCENTER);
131  {
132  S.StartMultiColumn(2, wxCENTER);
133  {
134  S.TieChoice(_("Bit Rate:"), wxT("/FileFormats/MP2Bitrate"),
135  160, mBitRateNames, mBitRateLabels);
136  }
137  S.EndMultiColumn();
138  }
139  S.EndHorizontalLay();
140  }
141  S.EndVerticalLay();
142 }
143 
146 bool ExportMP2Options::TransferDataToWindow()
147 {
148  return true;
149 }
150 
153 bool ExportMP2Options::TransferDataFromWindow()
154 {
155  ShuttleGui S(this, eIsSavingToPrefs);
156  PopulateOrExchange(S);
157 
158  gPrefs->Flush();
159 
160  return true;
161 }
162 
163 //----------------------------------------------------------------------------
164 // ExportMP2
165 //----------------------------------------------------------------------------
166 
167 class ExportMP2 final : public ExportPlugin
168 {
169 public:
170 
171  ExportMP2();
172 
173  // Required
174 
175  wxWindow *OptionsCreate(wxWindow *parent, int format) override;
177  std::unique_ptr<ProgressDialog> &pDialog,
178  unsigned channels,
179  const wxString &fName,
180  bool selectedOnly,
181  double t0,
182  double t1,
183  MixerSpec *mixerSpec = NULL,
184  const Tags *metadata = NULL,
185  int subformat = 0) override;
186 
187 private:
188 
189  int AddTags(AudacityProject *project, ArrayOf<char> &buffer, bool *endOfFile, const Tags *tags);
190 #ifdef USE_LIBID3TAG
191  void AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name);
192 #endif
193 
194 };
195 
196 ExportMP2::ExportMP2()
197 : ExportPlugin()
198 {
199  AddFormat();
200  SetFormat(wxT("MP2"),0);
201  AddExtension(wxT("mp2"),0);
202  SetMaxChannels(2,0);
203  SetCanMetaData(true,0);
204  SetDescription(_("MP2 Files"),0);
205 }
206 
207 ProgressResult ExportMP2::Export(AudacityProject *project,
208  std::unique_ptr<ProgressDialog> &pDialog,
209  unsigned channels, const wxString &fName,
210  bool selectionOnly, double t0, double t1, MixerSpec *mixerSpec, const Tags *metadata,
211  int WXUNUSED(subformat))
212 {
213  bool stereo = (channels == 2);
214  long bitrate = gPrefs->Read(wxT("/FileFormats/MP2Bitrate"), 160);
215  double rate = project->GetRate();
216  const TrackList *tracks = project->GetTracks();
217 
218  wxLogNull logNo; /* temporarily disable wxWidgets error messages */
219 
220  twolame_options *encodeOptions;
221  encodeOptions = twolame_init();
222  auto cleanup = finally( [&] { twolame_close(&encodeOptions); } );
223 
224  twolame_set_in_samplerate(encodeOptions, (int)(rate + 0.5));
225  twolame_set_out_samplerate(encodeOptions, (int)(rate + 0.5));
226  twolame_set_bitrate(encodeOptions, bitrate);
227  twolame_set_num_channels(encodeOptions, stereo ? 2 : 1);
228 
229  if (twolame_init_params(encodeOptions) != 0)
230  {
231  AudacityMessageBox(_("Cannot export MP2 with this sample rate and bit rate"),
232  _("Error"), wxICON_STOP);
234  }
235 
236  // Put ID3 tags at beginning of file
237  if (metadata == NULL)
238  metadata = project->GetTags();
239 
240  FileIO outFile(fName, FileIO::Output);
241  if (!outFile.IsOpened()) {
242  AudacityMessageBox(_("Unable to open target file for writing"));
244  }
245 
246  ArrayOf<char> id3buffer;
247  int id3len;
248  bool endOfFile;
249  id3len = AddTags(project, id3buffer, &endOfFile, metadata);
250  if (id3len && !endOfFile) {
251  if ( outFile.Write(id3buffer.get(), id3len).GetLastError() ) {
252  // TODO: more precise message
253  AudacityMessageBox(_("Unable to export"));
255  }
256  }
257 
258  // Values taken from the twolame simple encoder sample
259  const size_t pcmBufferSize = 9216 / 2; // number of samples
260  const size_t mp2BufferSize = 16384u; // bytes
261 
262  // We allocate a buffer which is twice as big as the
263  // input buffer, which should always be enough.
264  // We have to multiply by 4 because one sample is 2 bytes wide!
265  ArrayOf<unsigned char> mp2Buffer{ mp2BufferSize };
266 
267  const WaveTrackConstArray waveTracks =
268  tracks->GetWaveTrackConstArray(selectionOnly, false);
269  auto updateResult = ProgressResult::Success;
270  {
271  auto mixer = CreateMixer(waveTracks,
272  tracks->GetTimeTrack(),
273  t0, t1,
274  stereo ? 2 : 1, pcmBufferSize, true,
275  rate, int16Sample, true, mixerSpec);
276 
277  InitProgress( pDialog, wxFileName(fName).GetName(),
278  selectionOnly
279  ? wxString::Format(_("Exporting selected audio at %ld kbps"),
280  bitrate)
281  : wxString::Format(_("Exporting the audio at %ld kbps"),
282  bitrate) );
283  auto &progress = *pDialog;
284 
285  while (updateResult == ProgressResult::Success) {
286  auto pcmNumSamples = mixer->Process(pcmBufferSize);
287 
288  if (pcmNumSamples == 0)
289  break;
290 
291  short *pcmBuffer = (short *)mixer->GetBuffer();
292 
293  int mp2BufferNumBytes = twolame_encode_buffer_interleaved(
294  encodeOptions,
295  pcmBuffer,
296  pcmNumSamples,
297  mp2Buffer.get(),
298  mp2BufferSize);
299 
300  if (mp2BufferNumBytes < 0) {
301  // TODO: more precise message
302  AudacityMessageBox(_("Unable to export"));
303  updateResult = ProgressResult::Cancelled;
304  break;
305  }
306 
307  if ( outFile.Write(mp2Buffer.get(), mp2BufferNumBytes).GetLastError() ) {
308  // TODO: more precise message
309  AudacityMessageBox(_("Unable to export"));
311  }
312 
313  updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
314  }
315  }
316 
317  int mp2BufferNumBytes = twolame_encode_flush(
318  encodeOptions,
319  mp2Buffer.get(),
320  mp2BufferSize);
321 
322  if (mp2BufferNumBytes > 0)
323  if ( outFile.Write(mp2Buffer.get(), mp2BufferNumBytes).GetLastError() ) {
324  // TODO: more precise message
325  AudacityMessageBox(_("Unable to export"));
327  }
328 
329  /* Write ID3 tag if it was supposed to be at the end of the file */
330 
331  if (id3len && endOfFile)
332  if ( outFile.Write(id3buffer.get(), id3len).GetLastError() ) {
333  // TODO: more precise message
334  AudacityMessageBox(_("Unable to export"));
336  }
337 
338  if ( !outFile.Close() ) {
339  // TODO: more precise message
340  AudacityMessageBox(_("Unable to export"));
342  }
343 
344  return updateResult;
345 }
346 
347 wxWindow *ExportMP2::OptionsCreate(wxWindow *parent, int format)
348 {
349  wxASSERT(parent); // to justify safenew
350  return safenew ExportMP2Options(parent, format);
351 }
352 
353 
354 #ifdef USE_LIBID3TAG
355 struct id3_tag_deleter {
356  void operator () (id3_tag *p) const { if (p) id3_tag_delete(p); }
357 };
358 using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
359 #endif
360 
361 // returns buffer len; caller frees
362 int ExportMP2::AddTags(
363  AudacityProject * WXUNUSED(project), ArrayOf< char > &buffer,
364  bool *endOfFile, const Tags *tags)
365 {
366 #ifdef USE_LIBID3TAG
367  id3_tag_holder tp { id3_tag_new() };
368 
369  for (const auto &pair : tags->GetRange()) {
370  const auto &n = pair.first;
371  const auto &v = pair.second;
372  const char *name = "TXXX";
373 
374  if (n.CmpNoCase(TAG_TITLE) == 0) {
375  name = ID3_FRAME_TITLE;
376  }
377  else if (n.CmpNoCase(TAG_ARTIST) == 0) {
378  name = ID3_FRAME_ARTIST;
379  }
380  else if (n.CmpNoCase(TAG_ALBUM) == 0) {
381  name = ID3_FRAME_ALBUM;
382  }
383  else if (n.CmpNoCase(TAG_YEAR) == 0) {
384  // LLL: Some apps do not like the newer frame ID (ID3_FRAME_YEAR),
385  // so we add old one as well.
386  AddFrame(tp.get(), n, v, "TYER");
387  name = ID3_FRAME_YEAR;
388  }
389  else if (n.CmpNoCase(TAG_GENRE) == 0) {
390  name = ID3_FRAME_GENRE;
391  }
392  else if (n.CmpNoCase(TAG_COMMENTS) == 0) {
393  name = ID3_FRAME_COMMENT;
394  }
395  else if (n.CmpNoCase(TAG_TRACK) == 0) {
396  name = ID3_FRAME_TRACK;
397  }
398 
399  AddFrame(tp.get(), n, v, name);
400  }
401 
402  tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression
403 
404  // If this version of libid3tag supports it, use v2.3 ID3
405  // tags instead of the newer, but less well supported, v2.4
406  // that libid3tag uses by default.
407  #ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
408  tp->options |= ID3_TAG_OPTION_ID3V2_3;
409  #endif
410 
411  *endOfFile = false;
412 
413  id3_length_t len;
414 
415  len = id3_tag_render(tp.get(), 0);
416  buffer.reinit(len);
417  len = id3_tag_render(tp.get(), (id3_byte_t *)buffer.get());
418 
419 
420  return len;
421 #else //ifdef USE_LIBID3TAG
422  return 0;
423 #endif
424 }
425 
426 #ifdef USE_LIBID3TAG
427 void ExportMP2::AddFrame(struct id3_tag *tp, const wxString & n, const wxString & v, const char *name)
428 {
429  struct id3_frame *frame = id3_frame_new(name);
430 
431  if (!n.IsAscii() || !v.IsAscii()) {
432  id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
433  }
434  else {
435  id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
436  }
437 
439  id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) v.mb_str(wxConvUTF8)) };
440 
441  if (strcmp(name, ID3_FRAME_COMMENT) == 0) {
442  // A hack to get around iTunes not recognizing the comment. The
443  // language defaults to XXX and, since it's not a valid language,
444  // iTunes just ignores the tag. So, either set it to a valid language
445  // (which one???) or just clear it. Unfortunately, there's no supported
446  // way of clearing the field, so do it directly.
447  id3_field *f = id3_frame_field(frame, 1);
448  memset(f->immediate.value, 0, sizeof(f->immediate.value));
449  id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
450  }
451  else if (strcmp(name, "TXXX") == 0) {
452  id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
453 
454  ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) n.mb_str(wxConvUTF8)));
455 
456  id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
457  }
458  else {
459  auto addr = ucs4.get();
460  id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
461  }
462 
463  id3_tag_attachframe(tp, frame);
464 }
465 #endif
466 
467 std::unique_ptr<ExportPlugin> New_ExportMP2()
468 {
469  return std::make_unique<ExportMP2>();
470 }
471 
472 #endif // #ifdef USE_LIBTWOLAME
473 
wxChoice * TieChoice(const wxString &Prompt, WrappedType &WrappedRef, const wxArrayString *pChoices)
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
A list of TrackListNode items.
Definition: Track.h:618
ProgressResult
#define TAG_TRACK
Definition: Tags.h:63
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
std::unique_ptr< ExportPlugin > New_ExportMP2()
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:117
void EndMultiColumn()
#define TAG_TITLE
Definition: Tags.h:60
virtual wxWindow * OptionsCreate(wxWindow *parent, int format)=0
Definition: Export.cpp:215
int AudacityMessageBox(const wxString &message, const wxString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: ErrorDialog.h:92
#define TAG_ARTIST
Definition: Tags.h:61
TimeTrack * GetTimeTrack()
Definition: Track.cpp:1244
#define safenew
Definition: Audacity.h:230
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:417
void EndHorizontalLay()
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
void EndVerticalLay()
int format
Definition: ExportPCM.cpp:56
WaveTrackConstArray GetWaveTrackConstArray(bool selectionOnly, bool includeMuted=true) const
Definition: Track.cpp:1349
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
std::vector< std::shared_ptr< const WaveTrack > > WaveTrackConstArray
Definition: AudioIO.h:66
ID3 Tags (for MP3)
Definition: Tags.h:70
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop")).Raw()), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom")).Raw()), OnMoveTrack)#define SET_TRACK_NAME_PLUGIN_SYMBOLclass SetTrackNameCommand:public AudacityCommand
const wxChar * name
Definition: Distortion.cpp:94
#define TAG_COMMENTS
Definition: Tags.h:66
const Tags * GetTags()
Definition: Project.cpp:1453
#define TAG_GENRE
Definition: Tags.h:65
virtual ProgressResult Export(AudacityProject *project, std::unique_ptr< ProgressDialog > &pDialog, unsigned channels, const wxString &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.
double GetRate() const
Definition: Project.h:199
TrackList * GetTracks()
Definition: Project.h:192
Iterators GetRange() const
Definition: Tags.cpp:444
#define TAG_ALBUM
Definition: Tags.h:62
#define TAG_YEAR
Definition: Tags.h:64
Definition: FileIO.h:18
Class used with Mixer.
Definition: Mix.h:58
void StartVerticalLay(int iProp=1)