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/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 "../SelectFile.h"
36#include "../ShuttleGui.h"
37#include "../Tags.h"
38#include "Track.h"
39#include "float_cast.h"
40#include "../widgets/FileHistory.h"
41#include "../widgets/AudacityMessageBox.h"
42#include "../widgets/ProgressDialog.h"
43#include "../widgets/Warning.h"
44#include "wxFileNameWrapper.h"
45
46#ifdef USE_LIBID3TAG
47 #include <id3tag.h>
48 extern "C" {
49 struct id3_frame *id3_frame_new(char const *);
50 }
51#endif
52
53//----------------------------------------------------------------------------
54// ExportCLOptions
55//----------------------------------------------------------------------------
56
57class ExportCLOptions final : public wxPanelWrapper
58{
59public:
60 ExportCLOptions(wxWindow *parent, int format);
61 virtual ~ExportCLOptions();
62
64 bool TransferDataToWindow() override;
65 bool TransferDataFromWindow() override;
66
67 void OnBrowse(wxCommandEvent & event);
68
69private:
70 wxComboBox *mCmd;
72
73 DECLARE_EVENT_TABLE()
74};
75
76#define ID_BROWSE 5000
77
78BEGIN_EVENT_TABLE(ExportCLOptions, wxPanelWrapper)
81
82
84ExportCLOptions::ExportCLOptions(wxWindow *parent, int WXUNUSED(format))
85: wxPanelWrapper(parent, wxID_ANY)
86{
87 mHistory.Load(*gPrefs, wxT("/FileFormats/ExternalProgramHistory"));
88
89 if (mHistory.empty()) {
90 mHistory.Append(wxT("ffmpeg -i - \"%f.opus\""));
91 mHistory.Append(wxT("ffmpeg -i - \"%f.wav\""));
92 mHistory.Append(wxT("ffmpeg -i - \"%f\""));
93 mHistory.Append(wxT("lame - \"%f\""));
94 }
95
96 mHistory.Append(gPrefs->Read(wxT("/FileFormats/ExternalProgramExportCommand"),
97 mHistory[ 0 ]));
98
100 PopulateOrExchange(S);
101
102 TransferDataToWindow();
103
104 parent->Layout();
105}
106
108{
110}
111
115{
117 auto cmd = cmds[0];
118
119 S.StartVerticalLay();
120 {
121 S.StartHorizontalLay(wxEXPAND);
122 {
123 S.SetSizerProportion(1);
124 S.StartMultiColumn(3, wxEXPAND);
125 {
126 S.SetStretchyCol(1);
127 mCmd = S.AddCombo(XXO("Command:"),
128 cmd,
129 cmds);
130 S.Id(ID_BROWSE).AddButton(XXO("Browse..."),
131 wxALIGN_CENTER_VERTICAL);
132 S.AddFixedText( {} );
133 S.TieCheckBox(XXO("Show output"),
134 {wxT("/FileFormats/ExternalProgramShowOutput"),
135 false});
136 }
137 S.EndMultiColumn();
138 }
139 S.EndHorizontalLay();
140
141 S.AddTitle(XO(
142/* i18n-hint: Some programmer-oriented terminology here:
143 "Data" refers to the sound to be exported, "piped" means sent,
144 and "standard in" means the default input stream that the external program,
145 named by %f, will read. And yes, it's %f, not %s -- this isn't actually used
146 in the program as a format string. Keep %f unchanged. */
147"Data will be piped to standard in. \"%f\" uses the file name in the export window."), 250);
148 }
149 S.EndVerticalLay();
150}
151
155{
156 return true;
157}
158
162{
165
166 wxString cmd = mCmd->GetValue();
167
168 mHistory.Append(cmd);
170
171 gPrefs->Write(wxT("/FileFormats/ExternalProgramExportCommand"), cmd);
172 gPrefs->Flush();
173
174 return true;
175}
176
179void ExportCLOptions::OnBrowse(wxCommandEvent& WXUNUSED(event))
180{
181 wxString path;
182 FileExtension ext;
184
185#if defined(__WXMSW__)
186 ext = wxT("exe");
187 /* i18n-hint files that can be run as programs */
188 type = { XO("Executables"), { ext } };
189#endif
190
191 path = SelectFile(FileNames::Operation::Open,
192 XO("Find path to command"),
193 wxEmptyString,
194 wxEmptyString,
195 ext,
196 { type },
197 wxFD_OPEN | wxRESIZE_BORDER,
198 this);
199 if (path.empty()) {
200 return;
201 }
202
203 if (path.Find(wxT(' ')) == wxNOT_FOUND) {
204 mCmd->SetValue(path);
205 }
206 else {
207 mCmd->SetValue(wxT('"') + path + wxT('"'));
208 }
209
210 mCmd->SetInsertionPointEnd();
211
212 return;
213}
214
215//----------------------------------------------------------------------------
216// ExportCLProcess
217//----------------------------------------------------------------------------
218
219static void Drain(wxInputStream *s, wxString *o)
220{
221 while (s->CanRead()) {
222 char buffer[4096];
223
224 s->Read(buffer, WXSIZEOF(buffer) - 1);
225 buffer[s->LastRead()] = wxT('\0');
226 *o += LAT1CTOWX(buffer);
227 }
228}
229
230class ExportCLProcess final : public wxProcess
231{
232public:
233 ExportCLProcess(wxString *output)
234 {
235#if defined(__WXMAC__)
236 // Don't want to crash on broken pipe
237 signal(SIGPIPE, SIG_IGN);
238#endif
239
240 mOutput = output;
241 mActive = true;
242 mStatus = -555;
243 Redirect();
244 }
245
246 bool IsActive()
247 {
248 return mActive;
249 }
250
251 void OnTerminate(int WXUNUSED( pid ), int status)
252 {
253 Drain(GetInputStream(), mOutput);
254 Drain(GetErrorStream(), mOutput);
255
256 mStatus = status;
257 mActive = false;
258 }
259
261 {
262 return mStatus;
263 }
264
265private:
266 wxString *mOutput;
269};
270
271//----------------------------------------------------------------------------
272// ExportCL
273//----------------------------------------------------------------------------
274
275class ExportCL final : public ExportPlugin
276{
277public:
278
279 ExportCL();
280
281 // Required
282 void OptionsCreate(ShuttleGui &S, int format) override;
283
285 std::unique_ptr<BasicUI::ProgressDialog> &pDialog,
286 unsigned channels,
287 const wxFileNameWrapper &fName,
288 bool selectedOnly,
289 double t0,
290 double t1,
291 MixerSpec *mixerSpec = NULL,
292 const Tags *metadata = NULL,
293 int subformat = 0) override;
294
295 // Optional
296 bool CheckFileName(wxFileName &filename, int format = 0) override;
297
298private:
299 void GetSettings();
300
301 std::vector<char> GetMetaChunk(const Tags *metadata);
302 wxString mCmd;
303 bool mShow;
304
306 {
307#if defined(__WXMSW__)
308 wxString opath;
309
311 {
312 // Give Windows a chance at finding lame command in the default location.
313 wxString paths[] = {wxT("HKEY_LOCAL_MACHINE\\Software\\Lame for Audacity"),
314 wxT("HKEY_LOCAL_MACHINE\\Software\\FFmpeg for Audacity")};
315 wxString npath;
316 wxRegKey reg;
317
318 wxGetEnv(wxT("PATH"), &opath);
319 npath = opath;
320
321 for (int i = 0; i < WXSIZEOF(paths); i++) {
322 reg.SetName(paths[i]);
323
324 if (reg.Exists()) {
325 wxString ipath;
326 reg.QueryValue(wxT("InstallPath"), ipath);
327 if (!ipath.empty()) {
328 npath += wxPATH_SEP + ipath;
329 }
330 }
331 }
332
333 wxSetEnv(wxT("PATH"),npath);
334 };
335
337 {
338 if (!opath.empty())
339 {
340 wxSetEnv(wxT("PATH"),opath);
341 }
342 }
343#endif
344 };
345};
346
348: ExportPlugin()
349{
350 AddFormat();
351 SetFormat(wxT("CL"),0);
352 AddExtension(wxT(""),0);
353 SetMaxChannels(255,0);
354 SetCanMetaData(false,0);
355 SetDescription(XO("(external program)"),0);
356}
357
358ProgressResult ExportCL::Export(AudacityProject *project, std::unique_ptr<BasicUI::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( ProjectRate::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,
506 mixerSpec);
507
508 size_t numBytes = 0;
509 constSamplePtr 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();
534 if (numSamples == 0)
535 break;
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.Poll(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 using namespace std::chrono;
574 std::this_thread::sleep_for(10ms);
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
611std::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{
716 S.AddWindow( safenew ExportCLOptions{ S.GetParent(), format } );
717}
718
719bool 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};
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:82
void ShowExportErrorDialog(wxString ErrorCode, TranslatableString message, const TranslatableString &caption, bool allowReporting)
Definition: Export.cpp:1538
static void Drain(wxInputStream *s, wxString *o)
Definition: ExportCL.cpp:219
static Exporter::RegisteredExportPlugin sRegisteredPlugin
Definition: ExportCL.cpp:797
#define ID_BROWSE
Definition: ExportCL.cpp:76
int format
Definition: ExportPCM.cpp:56
wxString FileExtension
File extension, not including any leading dot.
Definition: Identifier.h:224
#define LAT1CTOWX(X)
Definition: Internat.h:160
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:146
#define safenew
Definition: MemoryX.h:10
FileConfig * gPrefs
Definition: Prefs.cpp:71
an object holding per-project preferred sample rate
@ floatSample
Definition: SampleFormat.h:34
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:44
const char * constSamplePtr
Definition: SampleFormat.h:50
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:39
@ eIsCreatingFromPrefs
Definition: ShuttleGui.h:48
@ eIsSavingToPrefs
Definition: ShuttleGui.h:49
#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:92
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
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:358
bool mShow
Definition: ExportCL.cpp:303
wxString mCmd
Definition: ExportCL.cpp:302
void OptionsCreate(ShuttleGui &S, int format) override
Definition: ExportCL.cpp:714
std::vector< char > GetMetaChunk(const Tags *metadata)
Definition: ExportCL.cpp:611
bool CheckFileName(wxFileName &filename, int format=0) override
Definition: ExportCL.cpp:719
void GetSettings()
Definition: ExportCL.cpp:790
void PopulateOrExchange(ShuttleGui &S)
Definition: ExportCL.cpp:114
virtual ~ExportCLOptions()
Definition: ExportCL.cpp:107
FileHistory mHistory
Definition: ExportCL.cpp:71
void OnBrowse(wxCommandEvent &event)
Definition: ExportCL.cpp:179
ExportCLOptions(wxWindow *parent, int format)
Definition: ExportCL.cpp:84
bool TransferDataFromWindow() override
Definition: ExportCL.cpp:161
bool TransferDataToWindow() override
Definition: ExportCL.cpp:154
wxComboBox * mCmd
Definition: ExportCL.cpp:70
void OnTerminate(int WXUNUSED(pid), int status)
Definition: ExportCL.cpp:251
ExportCLProcess(wxString *output)
Definition: ExportCL.cpp:233
wxString * mOutput
Definition: ExportCL.cpp:266
void AddExtension(const FileExtension &extension, int index)
Definition: Export.cpp:128
int AddFormat()
Add a NEW entry to the list of formats this plug-in can export.
Definition: Export.cpp:102
static void InitProgress(std::unique_ptr< BasicUI::ProgressDialog > &pDialog, const TranslatableString &title, const TranslatableString &message)
Definition: Export.cpp:252
void SetFormat(const wxString &format, int index)
Definition: Export.cpp:118
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:224
void SetDescription(const TranslatableString &description, int index)
Definition: Export.cpp:123
void SetCanMetaData(bool canmetadata, int index)
Definition: Export.cpp:148
void SetMaxChannels(unsigned maxchannels, unsigned index)
Definition: Export.cpp:143
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:71
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:628
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:486
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:145
BuiltinCommandsModule::Registration< CompareAudioCommand > reg