19#include <wx/cmdline.h>
20#include <wx/combobox.h>
23#include <wx/process.h>
25#include <wx/textctrl.h>
27#include <wx/msw/registry.h>
54 struct id3_frame *id3_frame_new(
char const *);
61void Drain(wxInputStream *s, wxString *o)
63 while (s->CanRead()) {
66 s->Read(buffer, WXSIZEOF(buffer) - 1);
67 buffer[s->LastRead()] =
wxT(
'\0');
80 wxString paths[] = {
wxT(
"HKEY_LOCAL_MACHINE\\Software\\Lame for Audacity"),
81 wxT(
"HKEY_LOCAL_MACHINE\\Software\\FFmpeg for Audacity")};
85 wxGetEnv(
wxT(
"PATH"), &opath);
88 for (
int i = 0; i < WXSIZEOF(paths); i++) {
89 reg.SetName(paths[i]);
93 reg.QueryValue(
wxT(
"InstallPath"), ipath);
95 npath += wxPATH_SEP + ipath;
100 wxSetEnv(
wxT(
"PATH"),npath);
107 wxSetEnv(
wxT(
"PATH"),opath);
122#if defined(__WXMAC__)
124 signal(SIGPIPE, SIG_IGN);
140 Drain(GetInputStream(), mOutput);
141 Drain(GetErrorStream(), mOutput);
200 mParent = wxGetTopLevelParent(
S.GetParent());
205 S.StartVerticalLay();
207 S.StartHorizontalLay(wxEXPAND);
209 S.SetSizerProportion(1);
210 S.StartMultiColumn(3, wxEXPAND);
216 mCommandBox->Bind(wxEVT_TEXT, [
this](wxCommandEvent& event) {
222 S.AddButton(
XXO(
"Browse..."), wxALIGN_CENTER_VERTICAL)
225 S.AddFixedText( {} );
230 S.EndHorizontalLay();
238 "Data will be piped to standard in. \"%f\" uses the file name in the export window."), 250);
245 wxArrayString
argv = wxCmdLineParser::ConvertStringToArgs(command,
246#
if defined(__WXMSW__)
249 wxCMD_LINE_SPLIT_UNIX
256 XO(
"Program name appears to be missing."),
262 wxFileName cmd(
argv[0]);
263 cmd.Normalize(wxPATH_NORM_ALL & ~wxPATH_NORM_ABSOLUTE);
266 if (cmd.IsAbsolute()) {
280 pathlist.AddEnvList(
wxT(
"PATH"));
281 wxString path = pathlist.FindAbsoluteValidPath(
argv[0]);
283 #if defined(__WXMSW__)
285 path = pathlist.FindAbsoluteValidPath(
argv[0] +
wxT(
".exe"));
319 return static_cast<int>(
CLOptions.size());
324 if(index >= 0 && index <
static_cast<int>(
CLOptions.size()))
336 value = std::string(
mCommand.ToUTF8());
351 mCommand = wxString::FromUTF8(*std::get_if<std::string>(&value));
382 #if defined(__WXMSW__)
385 type = {
XO(
"Executables"), { ext } };
389 XO(
"Find path to command"),
394 wxFD_OPEN | wxRESIZE_BORDER,
400 if (path.Find(
wxT(
' ')) != wxNOT_FOUND)
401 path =
wxT(
'"') + path +
wxT(
'"');
437 double t0,
double t1,
bool selectedOnly,
440 const Tags* tags)
override;
461 std::unique_ptr<ExportOptionsEditor>
476 wxT(
"CL"),
XO(
"(external program)"), {
""}, 255,
false
480std::unique_ptr<ExportOptionsEditor>
483 return std::make_unique<ExportOptionsCLEditor>();
488 return std::make_unique<CLExportProcessor>();
494 double t0,
double t1,
bool selectionOnly,
497 const Tags* metadata)
506 const auto path = fName.GetFullPath();
508 context.cmd = wxString::FromUTF8(ExportPluginHelpers::GetParameterValue<std::string>(parameters,
CLOptionIDCommand));
514 if(
context.cmd ==
wxT(
"ffmpeg -i - \"%f\"") && !fName.HasExt())
515 context.cmd.Replace(
"%f",
"%f.wav" );
519 context.process = std::make_unique<ExportCLProcess>(&
context.output);
536 const size_t maxBlockLen = 44100 * 5;
537 unsigned long totalSamples =
lrint((
t1 -
t0) * rate);
540 wxOutputStream *os =
process.GetOutputStream();
552 wxUint32 formatChunkLen;
556 wxUint32 avgBytesPerSec;
558 wxUint16 bitsPerSample;
573 riff.riffID[0] =
'R';
574 riff.riffID[1] =
'I';
575 riff.riffID[2] =
'F';
576 riff.riffID[3] =
'F';
577 riff.riffLen = wxUINT32_SWAP_ON_BE(
sizeof(riff) +
582 riff.riffType[0] =
'W';
583 riff.riffType[1] =
'A';
584 riff.riffType[2] =
'V';
585 riff.riffType[3] =
'E';
591 fmt.formatChunkLen = wxUINT32_SWAP_ON_BE(16);
592 fmt.formatTag = wxUINT16_SWAP_ON_BE(3);
593 fmt.channels = wxUINT16_SWAP_ON_BE(
channels);
594 fmt.sampleRate = wxUINT32_SWAP_ON_BE(rate);
596 fmt.blockAlign = wxUINT16_SWAP_ON_BE(fmt.bitsPerSample * fmt.channels / 8);
597 fmt.avgBytesPerSec = wxUINT32_SWAP_ON_BE(fmt.sampleRate * fmt.blockAlign);
600 if (metadata ==
nullptr) {
605 if (!metachunk.empty()) {
611 id3.id3Len = wxUINT32_SWAP_ON_BE(metachunk.size());
612 riff.riffLen +=
sizeof(id3) + metachunk.size();
615 data.dataID[0] =
'd';
616 data.dataID[1] =
'a';
617 data.dataID[2] =
't';
618 data.dataID[3] =
'a';
619 data.dataLen = wxUINT32_SWAP_ON_BE(sampleBytes);
622 os->Write(&riff,
sizeof(riff));
623 os->Write(&fmt,
sizeof(fmt));
624 if (!metachunk.empty()) {
625 os->Write(&id3,
sizeof(id3));
626 os->Write(metachunk.data(), metachunk.size());
628 os->Write(&data,
sizeof(data));
636 ?
XO(
"Exporting the selected audio using command-line encoder")
637 :
XO(
"Exporting the audio using command-line encoder");
651 wxOutputStream *os =
process.GetOutputStream();
652 auto closeIt =
finally ( [&] {
665 auto numSamples =
context.mixer->Process();
669 mixed =
context.mixer->GetBuffer();
670 numBytes = numSamples *
context.channels;
674#if wxBYTE_ORDER == wxBIG_ENDIAN
675 auto buffer = (
const float *) mixed;
676 for (
int i = 0; i < numBytes; i++) {
677 buffer[i] = wxUINT32_SWAP_ON_BE(buffer[i]);
684 size_t bytes = wxMin(numBytes, 4096);
688 os->Write(mixed, bytes);
693 bytes -= os->LastWrite();
694 mixed += os->LastWrite();
706 using namespace std::chrono;
707 std::this_thread::sleep_for(10ms);
716 wxDialogWrapper dlg(nullptr,
718 XO(
"Command Output"),
721 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
724 ShuttleGui S(&dlg, eIsCreating);
726 .Style( wxTE_MULTILINE | wxTE_READONLY | wxTE_RICH )
727 .AddTextWindow(cmd + wxT(
"\n\n") + output);
728 S.StartHorizontalLay(wxALIGN_CENTER, false);
730 S.Id(wxID_OK).AddButton(XXO(
"&OK"), wxALIGN_CENTER, true);
732 dlg.GetSizer()->AddSpacer(5);
734 dlg.SetMinSize(dlg.GetSize());
750 std::vector<char> buffer;
753 struct id3_tag_deleter {
754 void operator () (id3_tag *p)
const {
if (p) id3_tag_delete(p); }
757 std::unique_ptr<id3_tag, id3_tag_deleter> tp { id3_tag_new() };
759 for (
const auto &pair : tags->
GetRange()) {
760 const auto &n = pair.first;
761 const auto &v = pair.second;
762 const char *
name =
"TXXX";
765 name = ID3_FRAME_TITLE;
768 name = ID3_FRAME_ARTIST;
771 name = ID3_FRAME_ALBUM;
773 else if (n.CmpNoCase(
TAG_YEAR) == 0) {
774 name = ID3_FRAME_YEAR;
777 name = ID3_FRAME_GENRE;
780 name = ID3_FRAME_COMMENT;
783 name = ID3_FRAME_TRACK;
785 else if (n.CmpNoCase(
wxT(
"composer")) == 0) {
789 struct id3_frame *frame = id3_frame_new(
name);
791 if (!n.IsAscii() || !v.IsAscii()) {
792 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
795 id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
799 id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) v.mb_str(wxConvUTF8)) };
801 if (strcmp(
name, ID3_FRAME_COMMENT) == 0) {
807 id3_field *f = id3_frame_field(frame, 1);
808 memset(f->immediate.value, 0,
sizeof(f->immediate.value));
809 id3_field_setfullstring(id3_frame_field(frame, 3), ucs4.get());
811 else if (strcmp(
name,
"TXXX") == 0) {
812 id3_field_setstring(id3_frame_field(frame, 2), ucs4.get());
814 ucs4.reset(id3_utf8_ucs4duplicate((id3_utf8_t *) (
const char *) n.mb_str(wxConvUTF8)));
816 id3_field_setstring(id3_frame_field(frame, 1), ucs4.get());
819 auto addr = ucs4.get();
820 id3_field_setstrings(id3_frame_field(frame, 1), 1, &addr);
823 id3_tag_attachframe(tp.get(), frame);
826 tp->options &= (~ID3_TAG_OPTION_COMPRESSION);
831#ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
832 tp->options |= ID3_TAG_OPTION_ID3V2_3;
837 len = id3_tag_render(tp.get(), 0);
838 if ((len % 2) != 0) {
844 id3_tag_render(tp.get(), (id3_byte_t *) buffer.data());
852 []{
return std::make_unique< ExportCL >(); }
Toolkit-neutral facade for basic user interface services.
void ShowExportErrorDialog(const TranslatableString &message, const TranslatableString &caption, bool allowReporting)
const std::vector< ExportOption > CLOptions
static ExportPluginRegistry::RegisteredPlugin sRegisteredPlugin
std::variant< bool, int, double, std::string > ExportValue
A type of option values (parameters) used by exporting plugins.
XXO("&Cut/Copy/Paste Toolbar")
wxString FileExtension
File extension, not including any leading dot.
std::unique_ptr< Character[], freer > MallocString
audacity::BasicSettings * gPrefs
an object holding per-project preferred sample rate
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)
declares abstract base class Track, TrackList, and iterators over TrackList
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
ExportResult Process(ExportProcessorDelegate &delegate) override
struct CLExportProcessor::@143 context
TranslatableString status
static std::vector< char > GetMetaChunk(const Tags *metadata)
std::unique_ptr< Mixer > mixer
bool Initialize(AudacityProject &project, const Parameters ¶meters, const wxFileNameWrapper &filename, double t0, double t1, bool selectedOnly, double rate, unsigned channels, MixerOptions::Downmix *mixerSpec, const Tags *tags) override
Called before start processing.
std::unique_ptr< ExportCLProcess > process
int GetFormatCount() const override
FormatInfo GetFormatInfo(int) const override
Returns FormatInfo structure for given index if it's valid, or a default one. FormatInfo::format isn'...
std::unique_ptr< ExportOptionsEditor > CreateOptionsEditor(int, ExportOptionsEditor::Listener *) const override
Creates format-dependent options editor, that is used to create a valid set of parameters to be used ...
std::unique_ptr< ExportProcessor > CreateProcessor(int format) const override
ExportOptionsCLEditor()=default
bool GetValue(int id, ExportValue &value) const override
int GetOptionsCount() const override
bool SetValue(int id, const ExportValue &value) override
SampleRateList GetSampleRateList() const override
void PopulateUI(ShuttleGui &S) override
static bool IsValidCommand(const wxString &command)
void Store(audacity::BasicSettings &config) const override
bool TransferDataFromWindow() override
bool GetOption(int index, ExportOption &option) const override
void Load(const audacity::BasicSettings &config) override
void OnBrowse(const wxCommandEvent &)
Listener object that is used to report on option changes.
Editor objects are used to retrieve a set of export options, and configure exporting parameters accor...
std::vector< int > SampleRateList
static T GetParameterValue(const ExportProcessor::Parameters ¶meters, int id, T defaultValue=T())
static ExportResult UpdateProgress(ExportProcessorDelegate &delegate, Mixer &mixer, double t0, double t1)
Sends progress update to delegate and retrieves state update from it. Typically used inside each expo...
static std::unique_ptr< Mixer > CreateMixer(const AudacityProject &project, bool selectionOnly, double startTime, double stopTime, unsigned numOutChannels, size_t outBufferSize, bool outInterleaved, double outRate, sampleFormat outFormat, MixerOptions::Downmix *mixerSpec)
virtual void SetStatusString(const TranslatableString &str)=0
std::vector< std::tuple< ExportOptionID, ExportValue > > Parameters
Similar to wxFileHistory, but customized to our needs.
const_iterator begin() const
void Load(audacity::BasicSettings &config, const wxString &group=wxEmptyString)
void Append(const FilePath &file)
const_iterator end() const
void Save(audacity::BasicSettings &config)
FILES_API const FileType AllFiles
A matrix of booleans, one row per input channel, column per output.
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Holds a msgid for the translation catalog; may also bind format arguments.
void OnTerminate(int WXUNUSED(pid), int status) override
ExportCLProcess(wxString *output)
Base class for objects that provide facility to store data persistently, and access it with string ke...
virtual bool Write(const wxString &key, bool value)=0
virtual bool Read(const wxString &key, bool *value) const =0
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.
void CallAfter(Action action)
Schedule an action to be done later, and in the main thread.
void Yield()
Dispatch waiting events, including actions enqueued by CallAfter.
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
UTILITY_API const char *const * argv
A copy of argv; responsibility of application startup to assign it.
constexpr auto sampleRate
BuiltinCommandsModule::Registration< CompareAudioCommand > reg
void Drain(wxInputStream *s, wxString *o)
MessageBoxOptions && Caption(TranslatableString caption_) &&
MessageBoxOptions && IconStyle(Icon style) &&
A type that provides a description of an exporting option. Isn't allowed to change except non-type re...