Audacity  3.0.3
ExportCL.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ExportCL.cpp
6 
7  Joshua Haberman
8 
9  This code allows Audacity to export data by piping it to an external
10  program.
11 
12 **********************************************************************/
13 
14 
15 
16 #include "../ProjectSettings.h"
17 
18 #include <wx/app.h>
19 #include <wx/button.h>
20 #include <wx/cmdline.h>
21 #include <wx/combobox.h>
22 #include <wx/log.h>
23 #include <wx/process.h>
24 #include <wx/sizer.h>
25 #include <wx/textctrl.h>
26 #if defined(__WXMSW__)
27 #include <wx/msw/registry.h> // for wxRegKey
28 #endif
29 
30 #include "../FileNames.h"
31 #include "Export.h"
32 
33 #include "../Mix.h"
34 #include "../Prefs.h"
35 #include "../ShuttleGui.h"
36 #include "../Tags.h"
37 #include "../Track.h"
38 #include "../float_cast.h"
39 #include "../widgets/FileHistory.h"
40 #include "../widgets/AudacityMessageBox.h"
41 #include "../widgets/ProgressDialog.h"
42 #include "../widgets/Warning.h"
43 #include "../wxFileNameWrapper.h"
44 
45 #ifdef USE_LIBID3TAG
46  #include <id3tag.h>
47  extern "C" {
48  struct id3_frame *id3_frame_new(char const *);
49  }
50 #endif
51 
52 //----------------------------------------------------------------------------
53 // ExportCLOptions
54 //----------------------------------------------------------------------------
55 
56 class ExportCLOptions final : public wxPanelWrapper
57 {
58 public:
59  ExportCLOptions(wxWindow *parent, int format);
60  virtual ~ExportCLOptions();
61 
63  bool TransferDataToWindow() override;
64  bool TransferDataFromWindow() override;
65 
66  void OnBrowse(wxCommandEvent & event);
67 
68 private:
69  wxComboBox *mCmd;
71 
72  DECLARE_EVENT_TABLE()
73 };
74 
75 #define ID_BROWSE 5000
76 
77 BEGIN_EVENT_TABLE(ExportCLOptions, wxPanelWrapper)
80 
81 ExportCLOptions::ExportCLOptions(wxWindow *parent, int WXUNUSED(format))
84 : wxPanelWrapper(parent, wxID_ANY)
85 {
86  mHistory.Load(*gPrefs, wxT("/FileFormats/ExternalProgramHistory"));
87 
88  if (mHistory.empty()) {
89  mHistory.Append(wxT("ffmpeg -i - \"%f.opus\""));
90  mHistory.Append(wxT("ffmpeg -i - \"%f.wav\""));
91  mHistory.Append(wxT("ffmpeg -i - \"%f\""));
92  mHistory.Append(wxT("lame - \"%f\""));
93  }
94 
95  mHistory.Append(gPrefs->Read(wxT("/FileFormats/ExternalProgramExportCommand"),
96  mHistory[ 0 ]));
97 
99  PopulateOrExchange(S);
100 
101  TransferDataToWindow();
102 
103  parent->Layout();
104 }
105 
107 {
109 }
110 
114 {
116  auto cmd = cmds[0];
117 
118  S.StartVerticalLay();
119  {
120  S.StartHorizontalLay(wxEXPAND);
121  {
122  S.SetSizerProportion(1);
123  S.StartMultiColumn(3, wxEXPAND);
124  {
125  S.SetStretchyCol(1);
126  mCmd = S.AddCombo(XXO("Command:"),
127  cmd,
128  cmds);
129  S.Id(ID_BROWSE).AddButton(XXO("Browse..."),
130  wxALIGN_CENTER_VERTICAL);
131  S.AddFixedText( {} );
132  S.TieCheckBox(XXO("Show output"),
133  {wxT("/FileFormats/ExternalProgramShowOutput"),
134  false});
135  }
136  S.EndMultiColumn();
137  }
138  S.EndHorizontalLay();
139 
140  S.AddTitle(XO(
141 /* i18n-hint: Some programmer-oriented terminology here:
142  "Data" refers to the sound to be exported, "piped" means sent,
143  and "standard in" means the default input stream that the external program,
144  named by %f, will read. And yes, it's %f, not %s -- this isn't actually used
145  in the program as a format string. Keep %f unchanged. */
146 "Data will be piped to standard in. \"%f\" uses the file name in the export window."), 250);
147  }
148  S.EndVerticalLay();
149 }
150 
154 {
155  return true;
156 }
157 
161 {
162  ShuttleGui S(this, eIsSavingToPrefs);
164 
165  wxString cmd = mCmd->GetValue();
166 
167  mHistory.Append(cmd);
168  mHistory.Save(*gPrefs);
169 
170  gPrefs->Write(wxT("/FileFormats/ExternalProgramExportCommand"), cmd);
171  gPrefs->Flush();
172 
173  return true;
174 }
175 
178 void ExportCLOptions::OnBrowse(wxCommandEvent& WXUNUSED(event))
179 {
180  wxString path;
181  FileExtension ext;
183 
184 #if defined(__WXMSW__)
185  ext = wxT("exe");
186  /* i18n-hint files that can be run as programs */
187  type = { XO("Executables"), { ext } };
188 #endif
189 
190  path = FileNames::SelectFile(FileNames::Operation::Open,
191  XO("Find path to command"),
192  wxEmptyString,
193  wxEmptyString,
194  ext,
195  { type },
196  wxFD_OPEN | wxRESIZE_BORDER,
197  this);
198  if (path.empty()) {
199  return;
200  }
201 
202  if (path.Find(wxT(' ')) == wxNOT_FOUND) {
203  mCmd->SetValue(path);
204  }
205  else {
206  mCmd->SetValue(wxT('"') + path + wxT('"'));
207  }
208 
209  mCmd->SetInsertionPointEnd();
210 
211  return;
212 }
213 
214 //----------------------------------------------------------------------------
215 // ExportCLProcess
216 //----------------------------------------------------------------------------
217 
218 static void Drain(wxInputStream *s, wxString *o)
219 {
220  while (s->CanRead()) {
221  char buffer[4096];
222 
223  s->Read(buffer, WXSIZEOF(buffer) - 1);
224  buffer[s->LastRead()] = wxT('\0');
225  *o += LAT1CTOWX(buffer);
226  }
227 }
228 
229 class ExportCLProcess final : public wxProcess
230 {
231 public:
232  ExportCLProcess(wxString *output)
233  {
234 #if defined(__WXMAC__)
235  // Don't want to crash on broken pipe
236  signal(SIGPIPE, SIG_IGN);
237 #endif
238 
239  mOutput = output;
240  mActive = true;
241  mStatus = -555;
242  Redirect();
243  }
244 
245  bool IsActive()
246  {
247  return mActive;
248  }
249 
250  void OnTerminate(int WXUNUSED( pid ), int status)
251  {
252  Drain(GetInputStream(), mOutput);
253  Drain(GetErrorStream(), mOutput);
254 
255  mStatus = status;
256  mActive = false;
257  }
258 
259  int GetStatus()
260  {
261  return mStatus;
262  }
263 
264 private:
265  wxString *mOutput;
266  bool mActive;
267  int mStatus;
268 };
269 
270 //----------------------------------------------------------------------------
271 // ExportCL
272 //----------------------------------------------------------------------------
273 
274 class ExportCL final : public ExportPlugin
275 {
276 public:
277 
278  ExportCL();
279 
280  // Required
281  void OptionsCreate(ShuttleGui &S, int format) override;
282 
284  std::unique_ptr<ProgressDialog> &pDialog,
285  unsigned channels,
286  const wxFileNameWrapper &fName,
287  bool selectedOnly,
288  double t0,
289  double t1,
290  MixerSpec *mixerSpec = NULL,
291  const Tags *metadata = NULL,
292  int subformat = 0) override;
293 
294  // Optional
295  bool CheckFileName(wxFileName &filename, int format = 0) override;
296 
297 private:
298  void GetSettings();
299 
300  std::vector<char> GetMetaChunk(const Tags *metadata);
301  wxString mCmd;
302  bool mShow;
303 
304  struct ExtendPath
305  {
306 #if defined(__WXMSW__)
307  wxString opath;
308 
309  ExtendPath()
310  {
311  // Give Windows a chance at finding lame command in the default location.
312  wxString paths[] = {wxT("HKEY_LOCAL_MACHINE\\Software\\Lame for Audacity"),
313  wxT("HKEY_LOCAL_MACHINE\\Software\\FFmpeg for Audacity")};
314  wxString npath;
315  wxRegKey reg;
316 
317  wxGetEnv(wxT("PATH"), &opath);
318  npath = opath;
319 
320  for (int i = 0; i < WXSIZEOF(paths); i++) {
321  reg.SetName(paths[i]);
322 
323  if (reg.Exists()) {
324  wxString ipath;
325  reg.QueryValue(wxT("InstallPath"), ipath);
326  if (!ipath.empty()) {
327  npath += wxPATH_SEP + ipath;
328  }
329  }
330  }
331 
332  wxSetEnv(wxT("PATH"),npath);
333  };
334 
335  ~ExtendPath()
336  {
337  if (!opath.empty())
338  {
339  wxSetEnv(wxT("PATH"),opath);
340  }
341  }
342 #endif
343  };
344 };
345 
347 : ExportPlugin()
348 {
349  AddFormat();
350  SetFormat(wxT("CL"),0);
351  AddExtension(wxT(""),0);
352  SetMaxChannels(255,0);
353  SetCanMetaData(false,0);
354  SetDescription(XO("(external program)"),0);
355 }
356 
358  std::unique_ptr<ProgressDialog> &pDialog,
359  unsigned channels,
360  const wxFileNameWrapper &fName,
361  bool selectionOnly,
362  double t0,
363  double t1,
364  MixerSpec *mixerSpec,
365  const Tags *metadata,
366  int WXUNUSED(subformat))
367 {
368  ExtendPath ep;
369  wxString output;
370  long rc;
371 
372  const auto path = fName.GetFullPath();
373 
374  GetSettings();
375 
376  // Bug 2178 - users who don't know what they are doing will
377  // now get a file extension of .wav appended to their ffmpeg filename
378  // and therefore ffmpeg will be able to choose a file type.
379  if( mCmd == wxT("ffmpeg -i - \"%f\"") && !fName.HasExt())
380  mCmd.Replace( "%f", "%f.wav" );
381  mCmd.Replace(wxT("%f"), path);
382 
383  // Kick off the command
384  ExportCLProcess process(&output);
385 
386  rc = wxExecute(mCmd, wxEXEC_ASYNC, &process);
387  if (!rc) {
388  AudacityMessageBox( XO("Cannot export audio to %s").Format( path ) );
389  process.Detach();
390  process.CloseOutput();
391 
393  }
394 
395  // Turn off logging to prevent broken pipe messages
396  wxLogNull nolog;
397 
398  // establish parameters
399  int rate = lrint( ProjectSettings::Get( *project ).GetRate());
400  const size_t maxBlockLen = 44100 * 5;
401  unsigned long totalSamples = lrint((t1 - t0) * rate);
402  unsigned long sampleBytes = totalSamples * channels * SAMPLE_SIZE(floatSample);
403 
404  wxOutputStream *os = process.GetOutputStream();
405 
406  // RIFF header
407  struct {
408  char riffID[4]; // "RIFF"
409  wxUint32 riffLen; // basically the file len - 8
410  char riffType[4]; // "WAVE"
411  } riff;
412 
413  // format chunk */
414  struct {
415  char fmtID[4]; // "fmt " */
416  wxUint32 formatChunkLen; // (format chunk len - first two fields) 16 in our case
417  wxUint16 formatTag; // 1 for PCM
418  wxUint16 channels;
419  wxUint32 sampleRate;
420  wxUint32 avgBytesPerSec; // sampleRate * blockAlign
421  wxUint16 blockAlign; // bitsPerSample * channels (assume bps % 8 = 0)
422  wxUint16 bitsPerSample;
423  } fmt;
424 
425  // id3 chunk header
426  struct {
427  char id3ID[4]; // "id3 "
428  wxUint32 id3Len; // length of metadata in bytes
429  } id3;
430 
431  // data chunk header
432  struct {
433  char dataID[4]; // "data"
434  wxUint32 dataLen; // length of all samples in bytes
435  } data;
436 
437  riff.riffID[0] = 'R';
438  riff.riffID[1] = 'I';
439  riff.riffID[2] = 'F';
440  riff.riffID[3] = 'F';
441  riff.riffLen = wxUINT32_SWAP_ON_BE(sizeof(riff) +
442  sizeof(fmt) +
443  sizeof(data) +
444  sampleBytes -
445  8);
446  riff.riffType[0] = 'W';
447  riff.riffType[1] = 'A';
448  riff.riffType[2] = 'V';
449  riff.riffType[3] = 'E';
450 
451  fmt.fmtID[0] = 'f';
452  fmt.fmtID[1] = 'm';
453  fmt.fmtID[2] = 't';
454  fmt.fmtID[3] = ' ';
455  fmt.formatChunkLen = wxUINT32_SWAP_ON_BE(16);
456  fmt.formatTag = wxUINT16_SWAP_ON_BE(3);
457  fmt.channels = wxUINT16_SWAP_ON_BE(channels);
458  fmt.sampleRate = wxUINT32_SWAP_ON_BE(rate);
459  fmt.bitsPerSample = wxUINT16_SWAP_ON_BE(SAMPLE_SIZE(floatSample) * 8);
460  fmt.blockAlign = wxUINT16_SWAP_ON_BE(fmt.bitsPerSample * fmt.channels / 8);
461  fmt.avgBytesPerSec = wxUINT32_SWAP_ON_BE(fmt.sampleRate * fmt.blockAlign);
462 
463  // Retrieve tags if not given a set
464  if (metadata == NULL) {
465  metadata = &Tags::Get(*project);
466  }
467  auto metachunk = GetMetaChunk(metadata);
468 
469  if (metachunk.size()) {
470 
471  id3.id3ID[0] = 'i';
472  id3.id3ID[1] = 'd';
473  id3.id3ID[2] = '3';
474  id3.id3ID[3] = ' ';
475  id3.id3Len = wxUINT32_SWAP_ON_BE(metachunk.size());
476  riff.riffLen += sizeof(id3) + metachunk.size();
477  }
478 
479  data.dataID[0] = 'd';
480  data.dataID[1] = 'a';
481  data.dataID[2] = 't';
482  data.dataID[3] = 'a';
483  data.dataLen = wxUINT32_SWAP_ON_BE(sampleBytes);
484 
485  // write the headers and metadata
486  os->Write(&riff, sizeof(riff));
487  os->Write(&fmt, sizeof(fmt));
488  if (metachunk.size()) {
489  os->Write(&id3, sizeof(id3));
490  os->Write(metachunk.data(), metachunk.size());
491  }
492  os->Write(&data, sizeof(data));
493 
494  // Mix 'em up
495  const auto &tracks = TrackList::Get( *project );
496  auto mixer = CreateMixer(
497  tracks,
498  selectionOnly,
499  t0,
500  t1,
501  channels,
502  maxBlockLen,
503  true,
504  rate,
505  floatSample,
506  mixerSpec);
507 
508  size_t numBytes = 0;
509  samplePtr mixed = NULL;
510  auto updateResult = ProgressResult::Success;
511 
512  {
513  auto closeIt = finally ( [&] {
514  // Should make the process die, before propagating any exception
515  process.CloseOutput();
516  } );
517 
518  // Prepare the progress display
519  InitProgress( pDialog, XO("Export"),
520  selectionOnly
521  ? XO("Exporting the selected audio using command-line encoder")
522  : XO("Exporting the audio using command-line encoder") );
523  auto &progress = *pDialog;
524 
525  // Start piping the mixed data to the command
526  while (updateResult == ProgressResult::Success && process.IsActive() && os->IsOk()) {
527  // Capture any stdout and stderr from the command
528  Drain(process.GetInputStream(), &output);
529  Drain(process.GetErrorStream(), &output);
530 
531  // Need to mix another block
532  if (numBytes == 0) {
533  auto numSamples = mixer->Process(maxBlockLen);
534  if (numSamples == 0) {
535  break;
536  }
537 
538  mixed = mixer->GetBuffer();
539  numBytes = numSamples * channels;
540 
541  // Byte-swapping is necessary on big-endian machines, since
542  // WAV files are little-endian
543 #if wxBYTE_ORDER == wxBIG_ENDIAN
544  float *buffer = (float *) mixed;
545  for (int i = 0; i < numBytes; i++) {
546  buffer[i] = wxUINT32_SWAP_ON_BE(buffer[i]);
547  }
548 #endif
549  numBytes *= SAMPLE_SIZE(floatSample);
550  }
551 
552  // Don't write too much at once...pipes may not be able to handle it
553  size_t bytes = wxMin(numBytes, 4096);
554  numBytes -= bytes;
555 
556  while (bytes > 0) {
557  os->Write(mixed, bytes);
558  if (!os->IsOk()) {
559  updateResult = ProgressResult::Cancelled;
560  break;
561  }
562  bytes -= os->LastWrite();
563  mixed += os->LastWrite();
564  }
565 
566  // Update the progress display
567  updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
568  }
569  // Done with the progress display
570  }
571 
572  // Wait for process to terminate
573  while (process.IsActive()) {
574  wxMilliSleep(10);
575  wxTheApp->Yield();
576  }
577 
578  // Display output on error or if the user wants to see it
579  if (process.GetStatus() != 0 || mShow) {
580  // TODO use ShowInfoDialog() instead.
581  wxDialogWrapper dlg(nullptr,
582  wxID_ANY,
583  XO("Command Output"),
584  wxDefaultPosition,
585  wxSize(600, 400),
586  wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
587  dlg.SetName();
588 
589  ShuttleGui S(&dlg, eIsCreating);
590  S
591  .Style( wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH )
592  .AddTextWindow(mCmd + wxT("\n\n") + output);
593  S.StartHorizontalLay(wxALIGN_CENTER, false);
594  {
595  S.Id(wxID_OK).AddButton(XXO("&OK"), wxALIGN_CENTER, true);
596  }
597  dlg.GetSizer()->AddSpacer(5);
598  dlg.Layout();
599  dlg.SetMinSize(dlg.GetSize());
600  dlg.Center();
601 
602  dlg.ShowModal();
603 
604  if (process.GetStatus() != 0)
605  updateResult = ProgressResult::Failed;
606  }
607 
608  return updateResult;
609 }
610 
611 std::vector<char> ExportCL::GetMetaChunk(const Tags *tags)
612 {
613  std::vector<char> buffer;
614 
615 #ifdef USE_LIBID3TAG
616  struct id3_tag_deleter {
617  void operator () (id3_tag *p) const { if (p) id3_tag_delete(p); }
618  };
619 
620  std::unique_ptr<id3_tag, id3_tag_deleter> tp { id3_tag_new() };
621 
622  for (const auto &pair : tags->GetRange()) {
623  const auto &n = pair.first;
624  const auto &v = pair.second;
625  const char *name = "TXXX";
626 
627  if (n.CmpNoCase(TAG_TITLE) == 0) {
628  name = ID3_FRAME_TITLE;
629  }
630  else if (n.CmpNoCase(TAG_ARTIST) == 0) {
631  name = ID3_FRAME_ARTIST;
632  }
633  else if (n.CmpNoCase(TAG_ALBUM) == 0) {
634  name = ID3_FRAME_ALBUM;
635  }
636  else if (n.CmpNoCase(TAG_YEAR) == 0) {
637  name = ID3_FRAME_YEAR;
638  }
639  else if (n.CmpNoCase(TAG_GENRE) == 0) {
640  name = ID3_FRAME_GENRE;
641  }
642  else if (n.CmpNoCase(TAG_COMMENTS) == 0) {
643  name = ID3_FRAME_COMMENT;
644  }
645  else if (n.CmpNoCase(TAG_TRACK) == 0) {
646  name = ID3_FRAME_TRACK;
647  }
648  else if (n.CmpNoCase(wxT("composer")) == 0) {
649  name = "TCOM";
650  }
651 
652  struct id3_frame *frame = id3_frame_new(name);
653 
654  if (!n.IsAscii() || !v.IsAscii()) {
655  id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
656  }
657  else {
658  id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
659  }
660 
662  id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) v.mb_str(wxConvUTF8)) };
663 
664  if (strcmp(name, ID3_FRAME_COMMENT) == 0) {
665  // A hack to get around iTunes not recognizing the comment. The
666  // language defaults to XXX and, since it's not a valid language,
667  // iTunes just ignores the tag. So, either set it to a valid language
668  // (which one???) or just clear it. Unfortunately, there's no supported
669  // way of clearing the field, so do it directly.
670  id3_field *f = id3_frame_field(frame, 1);
671  memset(f->immediate.value, 0, sizeof(f->immediate.value));
672  id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
673  }
674  else if (strcmp(name, "TXXX") == 0) {
675  id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
676 
677  ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) n.mb_str(wxConvUTF8)));
678 
679  id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
680  }
681  else {
682  auto addr = ucs4.get();
683  id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
684  }
685 
686  id3_tag_attachframe(tp.get(), frame);
687  }
688 
689  tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression
690 
691  // If this version of libid3tag supports it, use v2.3 ID3
692  // tags instead of the newer, but less well supported, v2.4
693  // that libid3tag uses by default.
694 #ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
695  tp->options |= ID3_TAG_OPTION_ID3V2_3;
696 #endif
697 
698  id3_length_t len;
699 
700  len = id3_tag_render(tp.get(), 0);
701  if ((len % 2) != 0) {
702  len++; // Length must be even.
703  }
704 
705  if (len > 0) {
706  buffer.resize(len);
707  id3_tag_render(tp.get(), (id3_byte_t *) buffer.data());
708  }
709 #endif
710 
711  return buffer;
712 }
713 
715 {
717 }
718 
719 bool ExportCL::CheckFileName(wxFileName &filename, int WXUNUSED(format))
720 {
721  ExtendPath ep;
722 
723  if (filename.GetExt().empty()) {
724  if (ShowWarningDialog(NULL,
725  wxT("MissingExtension"),
726  XO("You've specified a file name without an extension. Are you sure?"),
727  true) == wxID_CANCEL) {
728  return false;
729  }
730  }
731 
732  GetSettings();
733 
734  wxArrayString argv = wxCmdLineParser::ConvertStringToArgs(mCmd,
735 #if defined(__WXMSW__)
736  wxCMD_LINE_SPLIT_DOS
737 #else
738  wxCMD_LINE_SPLIT_UNIX
739 #endif
740  );
741 
742  if (argv.size() == 0) {
744  ":745",
745  XO("Program name appears to be missing."));
746  return false;
747  }
748 
749  // Normalize the path (makes absolute and resolves variables)
750  wxFileName cmd(argv[0]);
751  cmd.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_ABSOLUTE);
752 
753  // Just verify the given path exists if it is absolute.
754  if (cmd.IsAbsolute()) {
755  if (!cmd.Exists()) {
757  XO("\"%s\" couldn't be found.").Format(cmd.GetFullPath()),
758  XO("Warning"),
759  wxOK | wxICON_EXCLAMATION);
760 
761  return false;
762  }
763 
764  return true;
765  }
766 
767  // Search for the command in the PATH list
768  wxPathList pathlist;
769  pathlist.AddEnvList(wxT("PATH"));
770  wxString path = pathlist.FindAbsoluteValidPath(argv[0]);
771 
772 #if defined(__WXMSW__)
773  if (path.empty()) {
774  path = pathlist.FindAbsoluteValidPath(argv[0] + wxT(".exe"));
775  }
776 #endif
777 
778  if (path.empty()) {
779  int action = AudacityMessageBox(
780  XO("Unable to locate \"%s\" in your path.").Format(cmd.GetFullPath()),
781  XO("Warning"),
782  wxOK | wxICON_EXCLAMATION);
783 
784  return false;
785  }
786 
787  return true;
788 }
789 
791 {
792  // Retrieve settings
793  gPrefs->Read(wxT("/FileFormats/ExternalProgramShowOutput"), &mShow, false);
794  mCmd = gPrefs->Read(wxT("/FileFormats/ExternalProgramExportCommand"), wxT("lame - \"%f.mp3\""));
795 }
796 
798  []{ return std::make_unique< ExportCL >(); }
799 };
EVT_BUTTON
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
FileExtension
wxString FileExtension
Definition: Types.h:267
eIsCreating
@ eIsCreating
Definition: ShuttleGui.h:36
FileHistory::begin
const_iterator begin() const
Definition: FileHistory.h:56
ShuttleGuiBase::StartVerticalLay
void StartVerticalLay(int iProp=1)
Definition: ShuttleGui.cpp:1177
ExportCLOptions::mHistory
FileHistory mHistory
Definition: ExportCL.cpp:70
wxFileNameWrapper
Definition: wxFileNameWrapper.h:21
ShuttleGuiBase::AddTitle
void AddTitle(const TranslatableString &Prompt, int wrapWidth=0)
Centred text string.
Definition: ShuttleGui.cpp:274
ExportCL::OptionsCreate
void OptionsCreate(ShuttleGui &S, int format) override
Definition: ExportCL.cpp:714
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:67
ExportCLOptions
Definition: ExportCL.cpp:57
AudacityMessageBox
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption=AudacityMessageBoxCaptionStr(), long style=wxOK|wxCENTRE, wxWindow *parent=NULL, int x=wxDefaultCoord, int y=wxDefaultCoord)
Definition: AudacityMessageBox.h:20
ExportPlugin::AddExtension
void AddExtension(const FileExtension &extension, int index)
Definition: Export.cpp:126
ShowExportErrorDialog
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption)
Definition: Export.cpp:1518
wxPanelWrapper
Definition: wxPanelWrapper.h:41
ExportCL
Definition: ExportCL.cpp:275
ShowWarningDialog
int ShowWarningDialog(wxWindow *parent, const wxString &internalDialogName, const TranslatableString &message, bool showCancelButton, const TranslatableString &footer)
Definition: Warning.cpp:93
Tags
ID3 Tags (for MP3)
Definition: Tags.h:74
ShuttleGuiBase::TieCheckBox
wxCheckBox * TieCheckBox(const TranslatableString &Prompt, bool &Var)
Definition: ShuttleGui.cpp:1603
ExportCLProcess::OnTerminate
void OnTerminate(int WXUNUSED(pid), int status)
Definition: ExportCL.cpp:250
Format
Abstract base class used in importing a file.
TAG_TRACK
#define TAG_TRACK
Definition: Tags.h:63
Tags::GetRange
Iterators GetRange() const
Definition: Tags.cpp:480
ID_BROWSE
#define ID_BROWSE
Definition: ExportCL.cpp:75
MallocString
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:349
XO
#define XO(s)
Definition: Internat.h:32
ProgressResult::Cancelled
@ Cancelled
ExportCL::GetMetaChunk
std::vector< char > GetMetaChunk(const Tags *metadata)
Definition: ExportCL.cpp:611
ProjectSettings::Get
static ProjectSettings & Get(AudacityProject &project)
Definition: ProjectSettings.cpp:39
FileHistory
Similar to wxFileHistory, but customized to our needs.
Definition: FileHistory.h:26
ShuttleGuiBase::EndMultiColumn
void EndMultiColumn()
Definition: ShuttleGui.cpp:1212
floatSample
@ floatSample
Definition: Types.h:722
FileNames::AllFiles
AUDACITY_DLL_API const FileType AllFiles
Definition: FileNames.h:74
wxArrayStringEx
Definition: MemoryX.h:663
Tags::Get
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:236
ShuttleGui::Id
ShuttleGui & Id(int id)
Definition: ShuttleGui.cpp:2248
ShuttleGui::Style
ShuttleGui & Style(long iStyle)
Definition: ShuttleGui.h:734
sRegisteredPlugin
static Exporter::RegisteredExportPlugin sRegisteredPlugin
Definition: ExportCL.cpp:797
ExportCLProcess::ExportCLProcess
ExportCLProcess(wxString *output)
Definition: ExportCL.cpp:232
ShuttleGuiBase::SetSizerProportion
void SetSizerProportion(int iProp)
Definition: ShuttleGui.h:498
ExportCL::CheckFileName
bool CheckFileName(wxFileName &filename, int format=0) override
Definition: ExportCL.cpp:719
samplePtr
char * samplePtr
Definition: Types.h:737
ProgressResult::Failed
@ Failed
XXO
#define XXO(s)
Definition: Internat.h:45
ShuttleGuiBase::EndHorizontalLay
void EndHorizontalLay()
Definition: ShuttleGui.cpp:1170
ExportCLProcess::mOutput
wxString * mOutput
Definition: ExportCL.cpp:265
ExportPlugin::InitProgress
static void InitProgress(std::unique_ptr< ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
Definition: Export.cpp:250
ProgressResult
ProgressResult
Definition: ProgressDialog.h:33
ShuttleGuiBase::StartHorizontalLay
void StartHorizontalLay(int PositionFlags=wxALIGN_CENTRE, int iProp=1)
Definition: ShuttleGui.cpp:1160
ProgressResult::Success
@ Success
ShuttleGuiBase::StartMultiColumn
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:1203
ExportPlugin::SetFormat
void SetFormat(const wxString &format, int index)
Definition: Export.cpp:116
ShuttleGuiBase::EndVerticalLay
void EndVerticalLay()
Definition: ShuttleGui.cpp:1196
ShuttleGuiBase::AddFixedText
void AddFixedText(const TranslatableString &Str, bool bCenter=false, int wrapWidth=0)
Definition: ShuttleGui.cpp:433
ExportCL::mCmd
wxString mCmd
Definition: ExportCL.cpp:301
name
const TranslatableString name
Definition: Distortion.cpp:98
TAG_GENRE
#define TAG_GENRE
Definition: Tags.h:65
format
int format
Definition: ExportPCM.cpp:54
ShuttleGuiBase::GetParent
wxWindow * GetParent()
Definition: ShuttleGui.h:503
Export.h
TAG_YEAR
#define TAG_YEAR
Definition: Tags.h:64
ExportPlugin::SetDescription
void SetDescription(const TranslatableString &description, int index)
Definition: Export.cpp:121
ExportCLProcess::IsActive
bool IsActive()
Definition: ExportCL.cpp:245
ExportCL::ExportCL
ExportCL()
Definition: ExportCL.cpp:346
ExportCLOptions::TransferDataToWindow
bool TransferDataToWindow() override
Definition: ExportCL.cpp:153
wxDialogWrapper::SetName
void SetName(const TranslatableString &title)
Definition: wxPanelWrapper.cpp:76
ExportCL::ExtendPath
Definition: ExportCL.cpp:305
ExportCLProcess::mActive
bool mActive
Definition: ExportCL.cpp:266
FileHistory::Append
void Append(const FilePath &file)
Definition: FileHistory.h:42
ShuttleGuiBase::AddButton
wxButton * AddButton(const TranslatableString &Text, int PositionFlags=wxALIGN_CENTRE, bool setDefault=false)
Definition: ShuttleGui.cpp:353
ExportCLOptions::ExportCLOptions
ExportCLOptions(wxWindow *parent, int format)
Definition: ExportCL.cpp:83
ExportPlugin::SetMaxChannels
void SetMaxChannels(unsigned maxchannels, unsigned index)
Definition: Export.cpp:141
eIsSavingToPrefs
@ eIsSavingToPrefs
Definition: ShuttleGui.h:46
LAT1CTOWX
#define LAT1CTOWX(X)
Definition: Internat.h:161
wxDialogWrapper
Definition: wxPanelWrapper.h:81
Exporter::RegisteredExportPlugin
Definition: Export.h:176
ExportCL::Export
ProgressResult Export(AudacityProject *project, std::unique_ptr< ProgressDialog > &pDialog, unsigned channels, const wxFileNameWrapper &fName, bool selectedOnly, double t0, double t1, MixerSpec *mixerSpec=NULL, const Tags *metadata=NULL, int subformat=0) override
called to export audio into a file.
Definition: ExportCL.cpp:357
ExportCL::mShow
bool mShow
Definition: ExportCL.cpp:302
Drain
static void Drain(wxInputStream *s, wxString *o)
Definition: ExportCL.cpp:218
ShuttleGuiBase::AddWindow
wxWindow * AddWindow(wxWindow *pWindow)
Definition: ShuttleGui.cpp:292
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:151
TrackList::Get
static TrackList & Get(AudacityProject &project)
Definition: Track.cpp:495
ExportCLProcess
Definition: ExportCL.cpp:230
FileNames::SelectFile
AUDACITY_DLL_API FilePath SelectFile(Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
FileHistory::end
const_iterator end() const
Definition: FileHistory.h:57
ExportCLOptions::~ExportCLOptions
virtual ~ExportCLOptions()
Definition: ExportCL.cpp:106
AudacityProject
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:112
ExportPlugin::CreateMixer
std::unique_ptr< Mixer > CreateMixer(const TrackList &tracks, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerSpec *mixerSpec)
Definition: Export.cpp:222
ExportCLProcess::mStatus
int mStatus
Definition: ExportCL.cpp:267
ProjectSettings::GetRate
double GetRate() const
Definition: ProjectSettings.cpp:166
ExportCL::GetSettings
void GetSettings()
Definition: ExportCL.cpp:790
ExportPlugin::SetCanMetaData
void SetCanMetaData(bool canmetadata, int index)
Definition: Export.cpp:146
ExportCLOptions::mCmd
wxComboBox * mCmd
Definition: ExportCL.cpp:69
TAG_COMMENTS
#define TAG_COMMENTS
Definition: Tags.h:66
TAG_ARTIST
#define TAG_ARTIST
Definition: Tags.h:61
ShuttleGuiBase::AddTextWindow
wxTextCtrl * AddTextWindow(const wxString &Value)
Multiline text box that grows.
Definition: ShuttleGui.cpp:705
ExportPlugin::AddFormat
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
Definition: Export.cpp:100
ExportCLOptions::OnBrowse
void OnBrowse(wxCommandEvent &event)
Definition: ExportCL.cpp:178
ExportCLProcess::GetStatus
int GetStatus()
Definition: ExportCL.cpp:259
ExportCLOptions::TransferDataFromWindow
bool TransferDataFromWindow() override
Definition: ExportCL.cpp:160
MixerSpec
Class used with Mixer.
Definition: Mix.h:57
eIsCreatingFromPrefs
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:45
ExportPlugin
Definition: Export.h:65
safenew
#define safenew
Definition: MemoryX.h:8
lrint
#define lrint(dbl)
Definition: float_cast.h:148
ShuttleGuiBase::SetStretchyCol
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:195
FileHistory::Save
void Save(wxConfigBase &config)
Definition: FileHistory.cpp:136
anonymous_namespace{CompareAudioCommand.cpp}::reg
BuiltinCommandsModule::Registration< CompareAudioCommand > reg
Definition: CompareAudioCommand.cpp:41
END_EVENT_TABLE
END_EVENT_TABLE()
ShuttleGuiBase::AddCombo
wxComboBox * AddCombo(const TranslatableString &Prompt, const wxString &Selected, const wxArrayStringEx &choices)
Definition: ShuttleGui.cpp:510
FileNames::FileType
Definition: FileNames.h:55
SAMPLE_SIZE
#define SAMPLE_SIZE(SampleFormat)
Definition: Types.h:732
TAG_TITLE
#define TAG_TITLE
Definition: Tags.h:60
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:638
ExportCLOptions::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S)
Definition: ExportCL.cpp:113
TAG_ALBUM
#define TAG_ALBUM
Definition: Tags.h:62