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