Audacity 3.2.0
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 <thread>
17
18#include <wx/app.h>
19#include <wx/cmdline.h>
20#include <wx/combobox.h>
21#include <wx/log.h>
22#include <wx/process.h>
23#include <wx/sizer.h>
24#include <wx/textctrl.h>
25#if defined(__WXMSW__)
26#include <wx/msw/registry.h> // for wxRegKey
27#endif
28
29#include "FileNames.h"
30#include "Export.h"
31
32#include "Mix.h"
33#include "Prefs.h"
34#include "SelectFile.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 "AudacityMessageBox.h"
41#include "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
56class ExportCLOptions final : public wxPanelWrapper
57{
58public:
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
68private:
69 wxComboBox *mCmd;
71
72 DECLARE_EVENT_TABLE()
73};
74
75#define ID_BROWSE 5000
76
77BEGIN_EVENT_TABLE(ExportCLOptions, wxPanelWrapper)
80
81
83ExportCLOptions::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{
164
165 wxString cmd = mCmd->GetValue();
166
167 mHistory.Append(cmd);
169
170 gPrefs->Write(wxT("/FileFormats/ExternalProgramExportCommand"), cmd);
171 gPrefs->Flush();
172
173 return true;
174}
175
178void 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 = 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
218static 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
229class ExportCLProcess final : public wxProcess
230{
231public:
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
260 {
261 return mStatus;
262 }
263
264private:
265 wxString *mOutput;
268};
269
270//----------------------------------------------------------------------------
271// ExportCL
272//----------------------------------------------------------------------------
273
274class ExportCL final : public ExportPlugin
275{
276public:
277
278 ExportCL();
279
280 // Required
281 void OptionsCreate(ShuttleGui &S, int format) override;
282
284 std::unique_ptr<BasicUI::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
297private:
298 void GetSettings();
299
300 std::vector<char> GetMetaChunk(const Tags *metadata);
301 wxString mCmd;
302 bool mShow;
303
305 {
306#if defined(__WXMSW__)
307 wxString opath;
308
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
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
357ProgressResult ExportCL::Export(AudacityProject *project, std::unique_ptr<BasicUI::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,
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();
533 if (numSamples == 0)
534 break;
535
536 mixed = mixer->GetBuffer();
537 numBytes = numSamples * channels;
538
539 // Byte-swapping is necessary on big-endian machines, since
540 // WAV files are little-endian
541#if wxBYTE_ORDER == wxBIG_ENDIAN
542 auto buffer = (const float *) mixed;
543 for (int i = 0; i < numBytes; i++) {
544 buffer[i] = wxUINT32_SWAP_ON_BE(buffer[i]);
545 }
546#endif
547 numBytes *= SAMPLE_SIZE(floatSample);
548 }
549
550 // Don't write too much at once...pipes may not be able to handle it
551 size_t bytes = wxMin(numBytes, 4096);
552 numBytes -= bytes;
553
554 while (bytes > 0) {
555 os->Write(mixed, bytes);
556 if (!os->IsOk()) {
557 updateResult = ProgressResult::Cancelled;
558 break;
559 }
560 bytes -= os->LastWrite();
561 mixed += os->LastWrite();
562 }
563
564 // Update the progress display
565 updateResult = progress.Poll(mixer->MixGetCurrentTime() - t0, t1 - t0);
566 }
567 // Done with the progress display
568 }
569
570 // Wait for process to terminate
571 while (process.IsActive()) {
572 using namespace std::chrono;
573 std::this_thread::sleep_for(10ms);
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
610std::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{
715 S.AddWindow( safenew ExportCLOptions{ S.GetParent(), format } );
716}
717
718bool 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};
wxT("CloseDown"))
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
END_EVENT_TABLE()
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:76
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption, bool allowReporting)
Definition: Export.cpp:1503
static void Drain(wxInputStream *s, wxString *o)
Definition: ExportCL.cpp:218
static Exporter::RegisteredExportPlugin sRegisteredPlugin
Definition: ExportCL.cpp:796
#define ID_BROWSE
Definition: ExportCL.cpp:75
int format
Definition: ExportPCM.cpp:53
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
wxString FileExtension
File extension, not including any leading dot.
Definition: Identifier.h:224
#define LAT1CTOWX(X)
Definition: Internat.h:158
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
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:50
const char * constSamplePtr
Definition: SampleFormat.h:56
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
@ eIsCreating
Definition: ShuttleGui.h:37
@ 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
int ShowWarningDialog(wxWindow *parent, const wxString &internalDialogName, const TranslatableString &message, bool showCancelButton, const TranslatableString &footer)
Definition: Warning.cpp:90
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
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) override
called to export audio into a file.
Definition: ExportCL.cpp:357
bool mShow
Definition: ExportCL.cpp:302
wxString mCmd
Definition: ExportCL.cpp:301
void OptionsCreate(ShuttleGui &S, int format) override
Definition: ExportCL.cpp:713
std::vector< char > GetMetaChunk(const Tags *metadata)
Definition: ExportCL.cpp:610
bool CheckFileName(wxFileName &filename, int format=0) override
Definition: ExportCL.cpp:718
void GetSettings()
Definition: ExportCL.cpp:789
void PopulateOrExchange(ShuttleGui &S)
Definition: ExportCL.cpp:113
virtual ~ExportCLOptions()
Definition: ExportCL.cpp:106
FileHistory mHistory
Definition: ExportCL.cpp:70
void OnBrowse(wxCommandEvent &event)
Definition: ExportCL.cpp:178
ExportCLOptions(wxWindow *parent, int format)
Definition: ExportCL.cpp:83
bool TransferDataFromWindow() override
Definition: ExportCL.cpp:160
bool TransferDataToWindow() override
Definition: ExportCL.cpp:153
wxComboBox * mCmd
Definition: ExportCL.cpp:69
void OnTerminate(int WXUNUSED(pid), int status)
Definition: ExportCL.cpp:250
ExportCLProcess(wxString *output)
Definition: ExportCL.cpp:232
wxString * mOutput
Definition: ExportCL.cpp:265
void AddExtension(const FileExtension &extension, int index)
Definition: Export.cpp:126
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
Definition: Export.cpp:100
static void InitProgress(std::unique_ptr< BasicUI::ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
Definition: Export.cpp:250
void SetFormat(const wxString &format, int index)
Definition: Export.cpp:116
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
void SetDescription(const TranslatableString &description, int index)
Definition: Export.cpp:121
void SetCanMetaData(bool canmetadata, int index)
Definition: Export.cpp:146
void SetMaxChannels(unsigned maxchannels, unsigned index)
Definition: Export.cpp:141
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
Similar to wxFileHistory, but customized to our needs.
Definition: FileHistory.h:26
const_iterator begin() const
Definition: FileHistory.h:56
void Append(const FilePath &file)
Definition: FileHistory.h:42
void Save(wxConfigBase &config)
const_iterator end() const
Definition: FileHistory.h:57
FILES_API const FileType AllFiles
Definition: FileNames.h:70
Abstract base class used in importing a file.
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
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:487
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
void SetName(const TranslatableString &title)
#define lrint(dbl)
Definition: float_cast.h:169
ProgressResult
Definition: BasicUI.h:147
UTILITY_API const char *const * argv
A copy of argv; responsibility of application startup to assign it.
BuiltinCommandsModule::Registration< CompareAudioCommand > reg