In another thread (https://forum.audacityteam.org/t/command-line-processing/15408/1) we talked about this and I started a new thread to present a “proof of concept”.
changes for unattended CLI operation
for unattended operation I think On Demand should (must?) be off
usage:
Audacity.exe -dounattended song.mp3
example:
create a “working” directory somewhere (here I am working in D:MP3sconverted)
open a CLI and cd to the “working” directory–this is the directory in which the file will be saved
run Audacity from the CLI with the -dounattended switch – only submit a single file to convert
D:MP3sconverted>“D:audioAudacityAudacity.exe” -dounattended D:MP3ssong.mp3
result:
D:MP3sconvertedsong.m4a is created with the user’s default M4A options
notes:
the new file gets the old file’s name with the extension changed to reflect the conversion
the complete path to Audacity must be specified as you do not want to be writing into your application directory
the fully qualified filename (complete with path and extension) must be used in the song name
without the -dounattended switch Audacity behaves as normal (except it will not nag to save an empty project)
to perform many of these conversions overnight create a script–either save it as a batch file and execute it or paste from a text editor into a CLI (I have tried pasting into a Win7 CommandLine and it does not work–using a batch file launches cmd.exe sequentially and works):
D:MP3sconverted>“D:audioAudacityAudacity.exe” -dounattended D:MP3ssong1.mp3
D:MP3sconverted>“D:audioAudacityAudacity.exe” -dounattended D:MP3ssong2.mp3
D:MP3sconverted>“D:audioAudacityAudacity.exe” -dounattended D:MP3ssong3.mp3
code changes are surrounded by pairs of comments with my initials:
//efm5
changed code
//efm5
below are all the changes with a few lines of context
line numbers below are approximations, correct to the SVN as of 20Aug2010
some of the reasoning is commented in the code,
some is described here
AudacityApp.h line #218:
public:
DECLARE_EVENT_TABLE()
//efm5
// store the current working directory as it exists when Audacity is launched
//(it gets hijacked somewhere -- see Project.cpp line #758
private:
wxString mCLIpath;
void SetCLIpath(const wxString & CLIpath) {mCLIpath = CLIpath;};
public:
const wxString & GetCLIpath() const {return mCLIpath;};
//efm5
};
extern AudacityApp & wxGetApp();
AudacityApp.cpp line #873:
bool AudacityApp::OnInit()
{
//efm5
SetCLIpath(wxGetCwd());
//efm5
#if defined(__WXGTK__)
project.h line #552
public:
DECLARE_EVENT_TABLE()
//efm5
public:
bool bStorageDirectorySet;//could use Get/Set() pair here
void UnattendedCLIAction();
//efm5
};
Project.cpp line #719:
AudacityProject::AudacityProject(wxWindow * parent, wxWindowID id,
[...]
mMenuClose(false),
//efm5
bDoUnattended(false)
//efm5
{ //this is line #759
//efm5
//note -- uncomment this to see that cwd has been hijacked by the time we get here as opposed to the real cwd from the cli (see AudacityApp.cpp line #875)
//wxMessageBox(wxGetCwd());
//AudacityApp & theApp = wxGetApp();
//wxMessageBox(theApp.GetCLIpath());
//efm5
the next turns off “save project” if it has no tracks and
also if it is running unattended
Project.cpp line #1862:
if (event.CanVeto() && (mEmptyCanBeDirty || bHasTracks)) {
if (mUndoManager.UnsavedChanges()) {
//efm5
if (!bDoUnattended) {
wxString Message = _("Save changes before closing?");
if( bHasTracks )
{
int result = wxMessageBox( Message,
_("Save changes?"),
wxYES_NO | wxCANCEL | wxICON_QUESTION,
this);
if (result == wxCANCEL || (result == wxYES && !Save()))
{
event.Veto();
return;
}
}
}//efm5
}
}
ModuleManager::Dispatch(ProjectClosing);
this next adds a new commandline switch to Audacity: -dounattended
it tells Audacity to excercise the new function for unattended operation
AudacityApp.cpp line #1189
if (!handled && !wxString(wxT("-version")).CmpNoCase(argv[option])) {
wxPrintf(wxT("Audacity v%s (%s)n"),
AUDACITY_VERSION_STRING,
(wxUSE_UNICODE ? wxT("Unicode") : wxT("ANSI")));
exit(0);
}
//efm5
if (!handled && !wxString(wxT("-dounattended")).CmpNoCase(argv[option])) {
if (!project)
project = CreateNewAudacityProject();
project->bDoUnattended = true;
handled = true;
}
//efm5
if (argv[option][0] == wxT('-') && !handled) {
wxPrintf(_("Unknown command line option: %sn"), argv[option]);
exit(0);
}
if (!handled)
{
if (!project)
{
// Create new window for project
project = CreateNewAudacityProject();
}
//efm5
wxString tempFN(GetCLIpath());
tempFN.Append(wxT("\"));//must escape NEWLINE to compile
tempFN.Append(argv[option]);
project->OpenFile(tempFN);
if (project->bDoUnattended)
{
wxSetWorkingDirectory(GetCLIpath());
project->UnattendedCLIAction();
}
//efm5
project = NULL; // don't reuse this project for other file
}
} // for option...
at this point we have a file opened in a project
the next step is to force the project to process (Export) the loaded file
I am going to import an MP3 file and Export it as an M4A
the export will be at my prefered Quality and will be stored in the CWD
Menus.h line# 180:
void OnExport();
//efm5
void OnExport(const wxString & FormatType, const wxString & FileName);
//efm5
void OnExportSelection();
Menus.cpp line# 2944
void AudacityProject::OnExport()
{
Exporter e;
e.Process(this, false, 0.0, mTracks->GetEndTime());
}
//efm5
void AudacityProject::OnExport(const wxString & FormatType, const wxString & FileName)
{
Exporter e;
e.Process(this, 2, FormatType, FileName, false, 0.0, mTracks->GetEndTime());
}
//efm5
void AudacityProject::OnExportSelection()
Project.h line# 552
public:
DECLARE_EVENT_TABLE()
//efm5
public:
bool bDoUnattended;//could use Get/Set() pair here
void UnattendedCLIAction();
//efm5
};
#endif
here is where we create our workflow
in this case I am exporting the file which was opened because it was passed as a CLI argument
I then tell Audacity to quit
Project.cpp line# 4551 (at end of file)
void AudacityProject::UnattendedCLIAction()
{
wxFileName fileName(GetFileName());
wxString tempName(fileName.GetName());
tempName.Append(wxT(".m4a"));
OnExport(wxT("M4A"), tempName);
OnExit();
}