AUP3: Fix macro path handling
Aside from the breakage I added to it during the AUP3 conversion, there was several other issues discovered in regards to how the export paths were being handled.
This commit is contained in:
parent
5c719e8d65
commit
9d749fef14
|
@ -25,7 +25,6 @@ processing. See also MacrosWindow and ApplyMacroDialog.
|
||||||
|
|
||||||
#include "Project.h"
|
#include "Project.h"
|
||||||
#include "ProjectAudioManager.h"
|
#include "ProjectAudioManager.h"
|
||||||
#include "ProjectFileIO.h"
|
|
||||||
#include "ProjectHistory.h"
|
#include "ProjectHistory.h"
|
||||||
#include "ProjectSettings.h"
|
#include "ProjectSettings.h"
|
||||||
#include "ProjectWindow.h"
|
#include "ProjectWindow.h"
|
||||||
|
@ -39,7 +38,6 @@ processing. See also MacrosWindow and ApplyMacroDialog.
|
||||||
#include "SelectUtilities.h"
|
#include "SelectUtilities.h"
|
||||||
#include "Shuttle.h"
|
#include "Shuttle.h"
|
||||||
#include "Track.h"
|
#include "Track.h"
|
||||||
#include "export/ExportMP3.h"
|
|
||||||
|
|
||||||
#include "AllThemeResources.h"
|
#include "AllThemeResources.h"
|
||||||
|
|
||||||
|
@ -47,46 +45,6 @@ processing. See also MacrosWindow and ApplyMacroDialog.
|
||||||
|
|
||||||
#include "commands/CommandContext.h"
|
#include "commands/CommandContext.h"
|
||||||
|
|
||||||
// KLUDGE: All commands should be on the same footing
|
|
||||||
// however, for historical reasons we distinguish between
|
|
||||||
// - Effects (which are looked up in effects lists)
|
|
||||||
// - Menu commands (which are held in command manager)
|
|
||||||
// - Specials (which we deal with specially here)
|
|
||||||
enum eCommandType { CtEffect, CtMenu, CtSpecial };
|
|
||||||
|
|
||||||
// TIDY-ME: Not currently translated,
|
|
||||||
// but there are issues to address if we do.
|
|
||||||
// CLEANSPEECH remnant
|
|
||||||
static const std::pair<TranslatableString, CommandID> SpecialCommands[] = {
|
|
||||||
// Use translations of the first members, some other day.
|
|
||||||
// For 2.2.2 we'll get them into the catalog at least.
|
|
||||||
|
|
||||||
{ XO("No Action"), wxT("NoAction") },
|
|
||||||
|
|
||||||
// { wxT("Import"), wxT("Import") }, // non-functioning
|
|
||||||
/* i18n-hint: before is adverb; MP3 names an audio file format */
|
|
||||||
{ XO("Export as MP3 56k before"), wxT("ExportMP3_56k_before") },
|
|
||||||
|
|
||||||
/* i18n-hint: after is adverb; MP3 names an audio file format */
|
|
||||||
{ XO("Export as MP3 56k after"), wxT("ExportMP3_56k_after") },
|
|
||||||
|
|
||||||
/* i18n-hint: FLAC names an audio file format */
|
|
||||||
{ XO("Export as FLAC"), wxT("ExportFLAC") },
|
|
||||||
|
|
||||||
// MP3 OGG and WAV already handled by menu items.
|
|
||||||
#if 0
|
|
||||||
/* i18n-hint: MP3 names an audio file format */
|
|
||||||
{ XO("Export as MP3"), wxT("ExportMP3") },
|
|
||||||
|
|
||||||
/* i18n-hint: Ogg names an audio file format */
|
|
||||||
{ XO("Export as Ogg"), wxT("ExportOgg") },
|
|
||||||
|
|
||||||
/* i18n-hint: WAV names an audio file format */
|
|
||||||
{ XO("Export as WAV"), wxT("ExportWAV") },
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
// end CLEANSPEECH remnant
|
|
||||||
|
|
||||||
MacroCommands::MacroCommands( AudacityProject &project )
|
MacroCommands::MacroCommands( AudacityProject &project )
|
||||||
: mProject{ project }
|
: mProject{ project }
|
||||||
, mExporter{ project }
|
, mExporter{ project }
|
||||||
|
@ -108,16 +66,12 @@ MacroCommands::MacroCommands( AudacityProject &project )
|
||||||
|
|
||||||
static const auto MP3Conversion = XO("MP3 Conversion");
|
static const auto MP3Conversion = XO("MP3 Conversion");
|
||||||
static const auto FadeEnds = XO("Fade Ends");
|
static const auto FadeEnds = XO("Fade Ends");
|
||||||
static const auto SelectToEnds = XO("Select to Ends");
|
|
||||||
|
|
||||||
|
|
||||||
wxArrayStringEx MacroCommands::GetNamesOfDefaultMacros()
|
wxArrayStringEx MacroCommands::GetNamesOfDefaultMacros()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
MP3Conversion.Translation() ,
|
MP3Conversion.Translation() ,
|
||||||
FadeEnds.Translation() ,
|
FadeEnds.Translation() ,
|
||||||
//Don't add this one anymore, as there is a new menu command for it.
|
|
||||||
//GetCustomTranslation( SelectToEnds ) ,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,10 +89,7 @@ void MacroCommands::RestoreMacro(const wxString & name)
|
||||||
AddToMacro( wxT("Select"), wxT("Start=\"0\" End=\"1\" RelativeTo=\"ProjectEnd\"") );
|
AddToMacro( wxT("Select"), wxT("Start=\"0\" End=\"1\" RelativeTo=\"ProjectEnd\"") );
|
||||||
AddToMacro( wxT("FadeOut") );
|
AddToMacro( wxT("FadeOut") );
|
||||||
AddToMacro( wxT("Select"), wxT("Start=\"0\" End=\"0\"") );
|
AddToMacro( wxT("Select"), wxT("Start=\"0\" End=\"0\"") );
|
||||||
} else if (name == SelectToEnds.Translation() ){
|
}
|
||||||
AddToMacro( wxT("SelCursorEnd") );
|
|
||||||
AddToMacro( wxT("SelStartCursor") );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CommandID MacroCommands::GetCommand(int index)
|
CommandID MacroCommands::GetCommand(int index)
|
||||||
|
@ -348,15 +299,7 @@ MacroCommandsCatalog::MacroCommandsCatalog( const AudacityProject *project )
|
||||||
if (!project)
|
if (!project)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// CLEANSPEECH remnant
|
|
||||||
Entries commands;
|
Entries commands;
|
||||||
for( const auto &command : SpecialCommands )
|
|
||||||
commands.push_back( {
|
|
||||||
{ command.second, command.first },
|
|
||||||
XO("Special Command")
|
|
||||||
} );
|
|
||||||
|
|
||||||
// end CLEANSPEECH remnant
|
|
||||||
|
|
||||||
PluginManager & pm = PluginManager::Get();
|
PluginManager & pm = PluginManager::Get();
|
||||||
EffectManager & em = EffectManager::Get();
|
EffectManager & em = EffectManager::Get();
|
||||||
|
@ -440,8 +383,7 @@ MacroCommandsCatalog::MacroCommandsCatalog( const AudacityProject *project )
|
||||||
|
|
||||||
// Sort commands by their user-visible names.
|
// Sort commands by their user-visible names.
|
||||||
// PRL: What exactly should happen if first members of pairs are not unique?
|
// PRL: What exactly should happen if first members of pairs are not unique?
|
||||||
// I'm not sure, but at least I can sort stably for a better defined result,
|
// I'm not sure, but at least I can sort stably for a better defined result.
|
||||||
// keeping specials before effects and menu items, and lastly commands.
|
|
||||||
auto less =
|
auto less =
|
||||||
[](const Entry &a, const Entry &b)
|
[](const Entry &a, const Entry &b)
|
||||||
{ return a.name.StrippedTranslation() <
|
{ return a.name.StrippedTranslation() <
|
||||||
|
@ -486,8 +428,6 @@ auto MacroCommandsCatalog::ByCommandId( const CommandID &commandId ) const
|
||||||
{ return entry.name.Internal() == commandId; });
|
{ return entry.name.Internal() == commandId; });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
wxString MacroCommands::GetCurrentParamsFor(const CommandID & command)
|
wxString MacroCommands::GetCurrentParamsFor(const CommandID & command)
|
||||||
{
|
{
|
||||||
const PluginID & ID =
|
const PluginID & ID =
|
||||||
|
@ -546,238 +486,6 @@ wxString MacroCommands::PromptForPresetFor(const CommandID & command, const wxSt
|
||||||
return preset;
|
return preset;
|
||||||
}
|
}
|
||||||
|
|
||||||
double MacroCommands::GetEndTime()
|
|
||||||
{
|
|
||||||
AudacityProject *project = &mProject;
|
|
||||||
if( project == NULL )
|
|
||||||
{
|
|
||||||
//AudacityMessageBox( XO("No project to process!") );
|
|
||||||
return -1.0;
|
|
||||||
}
|
|
||||||
auto &tracks = TrackList::Get( *project );
|
|
||||||
|
|
||||||
double endTime = tracks.GetEndTime();
|
|
||||||
return endTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MacroCommands::IsMono( AudacityProject *project )
|
|
||||||
{
|
|
||||||
if( project == NULL )
|
|
||||||
{
|
|
||||||
//AudacityMessageBox( XO("No project and no Audio to process!") );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto &tracks = TrackList::Get( *project );
|
|
||||||
|
|
||||||
return ( tracks.Any() - &Track::IsLeader ).empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString MacroCommands::BuildCleanFileName(const FilePath &fileName,
|
|
||||||
const FileExtension &extension)
|
|
||||||
{
|
|
||||||
const wxFileName newFileName{ fileName };
|
|
||||||
wxString justName = newFileName.GetName();
|
|
||||||
wxString pathName = newFileName.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
|
|
||||||
|
|
||||||
// PRL: should this translate?
|
|
||||||
const wxString cleanedString( "macro-output" );
|
|
||||||
|
|
||||||
if (justName.empty()) {
|
|
||||||
wxDateTime now = wxDateTime::Now();
|
|
||||||
int year = now.GetYear();
|
|
||||||
wxDateTime::Month month = now.GetMonth();
|
|
||||||
wxString monthName = now.GetMonthName(month);
|
|
||||||
int dom = now.GetDay();
|
|
||||||
int hour = now.GetHour();
|
|
||||||
int minute = now.GetMinute();
|
|
||||||
int second = now.GetSecond();
|
|
||||||
justName = wxString::Format(wxT("%d-%s-%02d-%02d-%02d-%02d"),
|
|
||||||
year, monthName, dom, hour, minute, second);
|
|
||||||
|
|
||||||
// SetName(cleanedFileName);
|
|
||||||
// bool isStereo;
|
|
||||||
// double endTime = project->mTracks->GetEndTime();
|
|
||||||
// double startTime = 0.0;
|
|
||||||
//OnSelectAll();
|
|
||||||
pathName = FileNames::FindDefaultPath(FileNames::Operation::Export);
|
|
||||||
::AudacityMessageBox(
|
|
||||||
XO("Export recording to %s\n/%s/%s.%s")
|
|
||||||
.Format(pathName, cleanedString, justName, extension),
|
|
||||||
XO("Export recording"),
|
|
||||||
wxOK | wxCENTRE);
|
|
||||||
pathName += wxFileName::GetPathSeparator();
|
|
||||||
}
|
|
||||||
wxString cleanedName = pathName;
|
|
||||||
cleanedName += cleanedString;
|
|
||||||
bool flag = ::wxFileName::FileExists(cleanedName);
|
|
||||||
if (flag == true) {
|
|
||||||
::AudacityMessageBox(
|
|
||||||
XO(
|
|
||||||
"Cannot create directory '%s'. \nFile already exists that is not a directory"),
|
|
||||||
Verbatim( cleanedName ) );
|
|
||||||
return wxString{};
|
|
||||||
}
|
|
||||||
::wxFileName::Mkdir(cleanedName, 0777, wxPATH_MKDIR_FULL); // make sure it exists
|
|
||||||
|
|
||||||
cleanedName += wxFileName::GetPathSeparator();
|
|
||||||
cleanedName += justName;
|
|
||||||
cleanedName += '.';
|
|
||||||
cleanedName += extension;
|
|
||||||
|
|
||||||
return cleanedName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Move this out of Batch Commands
|
|
||||||
bool MacroCommands::WriteMp3File( const wxString & Name, int bitrate )
|
|
||||||
{ //check if current project is mono or stereo
|
|
||||||
unsigned numChannels = 2;
|
|
||||||
if (IsMono( &mProject )) {
|
|
||||||
numChannels = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
double endTime = GetEndTime();
|
|
||||||
if( endTime <= 0.0f )
|
|
||||||
return false;
|
|
||||||
if( bitrate <=0 )
|
|
||||||
{
|
|
||||||
// 'No' bitrate given, use the current default.
|
|
||||||
// Use Mp3Stereo to control if export is to a stereo or mono file
|
|
||||||
return mExporter.Process(numChannels, wxT("MP3"), Name, false, 0.0, endTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool rc;
|
|
||||||
long prevBitRate = gPrefs->Read(wxT("/FileFormats/MP3Bitrate"), 128);
|
|
||||||
gPrefs->Write(wxT("/FileFormats/MP3Bitrate"), bitrate);
|
|
||||||
auto prevMode = MP3RateModeSetting.ReadEnum();
|
|
||||||
MP3RateModeSetting.WriteEnum(MODE_CBR);
|
|
||||||
|
|
||||||
auto cleanup = finally( [&] {
|
|
||||||
gPrefs->Write(wxT("/FileFormats/MP3Bitrate"), prevBitRate);
|
|
||||||
MP3RateModeSetting.WriteEnum(prevMode);
|
|
||||||
gPrefs->Flush();
|
|
||||||
} );
|
|
||||||
|
|
||||||
// Use Mp3Stereo to control if export is to a stereo or mono file
|
|
||||||
rc = mExporter.Process(numChannels, wxT("MP3"), Name, false, 0.0, endTime);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TIDY-ME: Get rid of special commands and make them part of the
|
|
||||||
// 'menu' system (but not showing on the menu)
|
|
||||||
//
|
|
||||||
// ======= IMPORTANT ========
|
|
||||||
// Special Commands are a KLUDGE whilst we wait for a better system to handle the menu
|
|
||||||
// commands from batch mode.
|
|
||||||
//
|
|
||||||
// Really we should be using a similar (or same) system to that used for effects
|
|
||||||
// so that parameters can be passed to the commands. Many of the menu
|
|
||||||
// commands take a selection as their parameter.
|
|
||||||
//
|
|
||||||
// If you find yourself adding lots of existing commands from the menus here, STOP
|
|
||||||
// and think again.
|
|
||||||
// ======= IMPORTANT ========
|
|
||||||
// CLEANSPEECH remnant
|
|
||||||
bool MacroCommands::ApplySpecialCommand(
|
|
||||||
int WXUNUSED(iCommand), const TranslatableString &friendlyCommand,
|
|
||||||
const CommandID & command, const wxString & params)
|
|
||||||
{
|
|
||||||
if (ReportAndSkip(friendlyCommand, params))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
AudacityProject *project = &mProject;
|
|
||||||
auto &projectFileIO = ProjectFileIO::Get( *project );
|
|
||||||
|
|
||||||
unsigned numChannels = 1; //used to switch between mono and stereo export
|
|
||||||
if (IsMono( &mProject )) {
|
|
||||||
numChannels = 1; //export in mono
|
|
||||||
} else {
|
|
||||||
numChannels = 2; //export in stereo
|
|
||||||
}
|
|
||||||
|
|
||||||
wxString filename;
|
|
||||||
FileExtension extension; // required for correct message
|
|
||||||
if (command == wxT("ExportWAV"))
|
|
||||||
extension = wxT("wav");
|
|
||||||
else if (command == wxT("ExportOgg"))
|
|
||||||
extension = wxT("ogg");
|
|
||||||
else if (command == wxT("ExportFLAC"))
|
|
||||||
extension = wxT("flac");
|
|
||||||
else extension = wxT("mp3");
|
|
||||||
|
|
||||||
if (mFileName.empty()) {
|
|
||||||
filename = BuildCleanFileName(projectFileIO.GetFileName(), extension);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
filename = BuildCleanFileName(mFileName, extension);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wxString cleanedString("macro-output");
|
|
||||||
// We have a command index, but we don't use it!
|
|
||||||
// TODO: Make this special-batch-command code use the menu item code....
|
|
||||||
// FIXME: TRAP_ERR No error reporting on write file failure in batch mode.
|
|
||||||
if (command == wxT("NoAction")) {
|
|
||||||
return true;
|
|
||||||
} else if (!mFileName.empty() && command == wxT("Import")) {
|
|
||||||
// historically this was in use, now ignored if there
|
|
||||||
return true;
|
|
||||||
} else if (command == wxT("ExportMP3_56k_before")) {
|
|
||||||
#if defined(__WXMSW__)
|
|
||||||
filename.Replace(cleanedString + wxT("\\"), cleanedString + wxT("\\MasterBefore_"), false);
|
|
||||||
#else
|
|
||||||
filename.Replace(cleanedString + wxT("/"), cleanedString + wxT("/MasterBefore_"), false);
|
|
||||||
#endif
|
|
||||||
return WriteMp3File(filename, 56);
|
|
||||||
} else if (command == wxT("ExportMP3_56k_after")) {
|
|
||||||
#if defined(__WXMSW__)
|
|
||||||
filename.Replace(cleanedString + wxT("\\"), cleanedString + wxT("\\MasterAfter_"), false);
|
|
||||||
#else
|
|
||||||
filename.Replace(cleanedString + wxT("/"), cleanedString + wxT("/MasterAfter_"), false);
|
|
||||||
#endif
|
|
||||||
return WriteMp3File(filename, 56);
|
|
||||||
} else if (command == wxT("ExportMP3")) {
|
|
||||||
return WriteMp3File(filename, 0); // 0 bitrate means use default/current
|
|
||||||
} else if (command == wxT("ExportWAV")) {
|
|
||||||
filename.Replace(wxT(".mp3"), wxT(".wav"), false);
|
|
||||||
double endTime = GetEndTime();
|
|
||||||
if (endTime <= 0.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mExporter.Process(numChannels, wxT("WAV"), filename, false, 0.0, endTime);
|
|
||||||
} else if (command == wxT("ExportOgg")) {
|
|
||||||
#ifdef USE_LIBVORBIS
|
|
||||||
filename.Replace(wxT(".mp3"), wxT(".ogg"), false);
|
|
||||||
double endTime = GetEndTime();
|
|
||||||
if (endTime <= 0.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mExporter.Process(numChannels, wxT("OGG"), filename, false, 0.0, endTime);
|
|
||||||
#else
|
|
||||||
AudacityMessageBox( XO(
|
|
||||||
"Ogg Vorbis support is not included in this build of Audacity"));
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
} else if (command == wxT("ExportFLAC")) {
|
|
||||||
#ifdef USE_LIBFLAC
|
|
||||||
filename.Replace(wxT(".mp3"), wxT(".flac"), false);
|
|
||||||
double endTime = GetEndTime();
|
|
||||||
if (endTime <= 0.0f) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return mExporter.Process(numChannels, wxT("FLAC"), filename, false, 0.0, endTime);
|
|
||||||
#else
|
|
||||||
AudacityMessageBox(XO(
|
|
||||||
"FLAC support is not included in this build of Audacity"));
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
AudacityMessageBox(
|
|
||||||
XO("Command %s not implemented yet").Format( friendlyCommand ) );
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// end CLEANSPEECH remnant
|
|
||||||
|
|
||||||
/// DoAudacityCommand() takes a PluginID and executes the associated command.
|
/// DoAudacityCommand() takes a PluginID and executes the associated command.
|
||||||
///
|
///
|
||||||
/// At the moment flags are used only to indicate whether to prompt for
|
/// At the moment flags are used only to indicate whether to prompt for
|
||||||
|
@ -909,15 +617,6 @@ bool MacroCommands::ApplyCommand( const TranslatableString &friendlyCommand,
|
||||||
const CommandID & command, const wxString & params,
|
const CommandID & command, const wxString & params,
|
||||||
CommandContext const * pContext)
|
CommandContext const * pContext)
|
||||||
{
|
{
|
||||||
|
|
||||||
// Test for a special command.
|
|
||||||
// CLEANSPEECH remnant
|
|
||||||
for( size_t i = 0; i < WXSIZEOF( SpecialCommands ); ++i ) {
|
|
||||||
if( command == SpecialCommands[i].second )
|
|
||||||
return ApplySpecialCommand( i, friendlyCommand, command, params );
|
|
||||||
}
|
|
||||||
// end CLEANSPEECH remnant
|
|
||||||
|
|
||||||
// Test for an effect.
|
// Test for an effect.
|
||||||
const PluginID & ID =
|
const PluginID & ID =
|
||||||
EffectManager::Get().GetEffectByIdentifier( command );
|
EffectManager::Get().GetEffectByIdentifier( command );
|
||||||
|
|
|
@ -72,9 +72,6 @@ class MacroCommands final {
|
||||||
bool ApplyCommandInBatchMode( const TranslatableString &friendlyCommand,
|
bool ApplyCommandInBatchMode( const TranslatableString &friendlyCommand,
|
||||||
const CommandID & command, const wxString ¶ms,
|
const CommandID & command, const wxString ¶ms,
|
||||||
CommandContext const * pContext = NULL);
|
CommandContext const * pContext = NULL);
|
||||||
bool ApplySpecialCommand(
|
|
||||||
int iCommand, const TranslatableString &friendlyCommand,
|
|
||||||
const CommandID & command, const wxString & params);
|
|
||||||
bool ApplyEffectCommand(
|
bool ApplyEffectCommand(
|
||||||
const PluginID & ID, const TranslatableString &friendlyCommand,
|
const PluginID & ID, const TranslatableString &friendlyCommand,
|
||||||
const CommandID & command,
|
const CommandID & command,
|
||||||
|
@ -82,13 +79,6 @@ class MacroCommands final {
|
||||||
bool ReportAndSkip( const TranslatableString & friendlyCommand, const wxString & params );
|
bool ReportAndSkip( const TranslatableString & friendlyCommand, const wxString & params );
|
||||||
void AbortBatch();
|
void AbortBatch();
|
||||||
|
|
||||||
// Utility functions for the special commands.
|
|
||||||
static wxString BuildCleanFileName(const FilePath &fileName,
|
|
||||||
const FileExtension &extension);
|
|
||||||
bool WriteMp3File( const wxString & Name, int bitrate );
|
|
||||||
double GetEndTime();
|
|
||||||
static bool IsMono( AudacityProject *project );
|
|
||||||
|
|
||||||
// These commands do not depend on the command list.
|
// These commands do not depend on the command list.
|
||||||
static void MigrateLegacyChains();
|
static void MigrateLegacyChains();
|
||||||
static wxArrayString GetNames();
|
static wxArrayString GetNames();
|
||||||
|
|
|
@ -148,6 +148,19 @@ void AudacityProject::SetProjectName(const wxString &name)
|
||||||
mName = name;
|
mName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilePath AudacityProject::GetInitialImportPath() const
|
||||||
|
{
|
||||||
|
return mInitialImportPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudacityProject::SetInitialImportPath(const FilePath &path)
|
||||||
|
{
|
||||||
|
if (mInitialImportPath.empty())
|
||||||
|
{
|
||||||
|
mInitialImportPath = path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
AUDACITY_DLL_API wxFrame &GetProjectFrame( AudacityProject &project )
|
AUDACITY_DLL_API wxFrame &GetProjectFrame( AudacityProject &project )
|
||||||
{
|
{
|
||||||
auto ptr = project.GetFrame();
|
auto ptr = project.GetFrame();
|
||||||
|
|
|
@ -138,7 +138,12 @@ class AUDACITY_DLL_API AudacityProject final
|
||||||
const wxString &GetProjectName() const;
|
const wxString &GetProjectName() const;
|
||||||
void SetProjectName(const wxString &name);
|
void SetProjectName(const wxString &name);
|
||||||
|
|
||||||
private:
|
// Used exclusively in batch mode, this allows commands to remember
|
||||||
|
// and use the initial import path
|
||||||
|
FilePath GetInitialImportPath() const;
|
||||||
|
void SetInitialImportPath(const FilePath &path);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
// The project's name
|
// The project's name
|
||||||
wxString mName;
|
wxString mName;
|
||||||
|
@ -146,6 +151,8 @@ class AUDACITY_DLL_API AudacityProject final
|
||||||
static int mProjectCounter;// global counter.
|
static int mProjectCounter;// global counter.
|
||||||
int mProjectNo; // count when this project was created.
|
int mProjectNo; // count when this project was created.
|
||||||
|
|
||||||
|
FilePath mInitialImportPath;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool mbBusyImporting{ false }; // used to fix bug 584
|
bool mbBusyImporting{ false }; // used to fix bug 584
|
||||||
int mBatchMode{ 0 };// 0 means not, >0 means in batch mode.
|
int mBatchMode{ 0 };// 0 means not, >0 means in batch mode.
|
||||||
|
|
|
@ -1116,6 +1116,7 @@ ProjectFileManager::AddImportedTracks(const FilePath &fileName,
|
||||||
// the filename to the just imported path.
|
// the filename to the just imported path.
|
||||||
if (initiallyEmpty && projectFileIO.IsTemporary()) {
|
if (initiallyEmpty && projectFileIO.IsTemporary()) {
|
||||||
project.SetProjectName(fn.GetName());
|
project.SetProjectName(fn.GetName());
|
||||||
|
project.SetInitialImportPath(fn.GetPath());
|
||||||
projectFileIO.SetProjectTitle();
|
projectFileIO.SetProjectTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1127,7 +1128,9 @@ ProjectFileManager::AddImportedTracks(const FilePath &fileName,
|
||||||
|
|
||||||
// If pNewTrackList is passed in non-NULL, it gets filled with the pointers to NEW tracks.
|
// If pNewTrackList is passed in non-NULL, it gets filled with the pointers to NEW tracks.
|
||||||
bool ProjectFileManager::Import(
|
bool ProjectFileManager::Import(
|
||||||
const FilePath &fileName, WaveTrackArray* pTrackArray /*= NULL*/)
|
const FilePath &fileName,
|
||||||
|
WaveTrackArray *pTrackArray /* = nullptr */,
|
||||||
|
bool addToHistory /* = true */)
|
||||||
{
|
{
|
||||||
auto &project = mProject;
|
auto &project = mProject;
|
||||||
auto &projectFileIO = ProjectFileIO::Get(project);
|
auto &projectFileIO = ProjectFileIO::Get(project);
|
||||||
|
@ -1145,13 +1148,17 @@ bool ProjectFileManager::Import(
|
||||||
// If the project was clean and temporary (not permanently saved), then set
|
// If the project was clean and temporary (not permanently saved), then set
|
||||||
// the filename to the just imported path.
|
// the filename to the just imported path.
|
||||||
if (initiallyEmpty && projectFileIO.IsTemporary()) {
|
if (initiallyEmpty && projectFileIO.IsTemporary()) {
|
||||||
project.SetProjectName(wxFileName(fileName).GetName());
|
wxFileName fn(fileName);
|
||||||
|
project.SetProjectName(fn.GetName());
|
||||||
|
project.SetInitialImportPath(fn.GetPath());
|
||||||
projectFileIO.SetProjectTitle();
|
projectFileIO.SetProjectTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
history.PushState(XO("Imported '%s'").Format(fileName), XO("Import"));
|
history.PushState(XO("Imported '%s'").Format(fileName), XO("Import"));
|
||||||
|
|
||||||
FileHistory::Global().Append(fileName);
|
if (addToHistory) {
|
||||||
|
FileHistory::Global().Append(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -1182,7 +1189,9 @@ bool ProjectFileManager::Import(
|
||||||
if (!success)
|
if (!success)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
FileHistory::Global().Append(fileName);
|
if (addToHistory) {
|
||||||
|
FileHistory::Global().Append(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
// no more errors, commit
|
// no more errors, commit
|
||||||
committed = true;
|
committed = true;
|
||||||
|
@ -1204,7 +1213,9 @@ bool ProjectFileManager::Import(
|
||||||
// If the project was clean and temporary (not permanently saved), then set
|
// If the project was clean and temporary (not permanently saved), then set
|
||||||
// the filename to the just imported path.
|
// the filename to the just imported path.
|
||||||
if (initiallyEmpty && projectFileIO.IsTemporary()) {
|
if (initiallyEmpty && projectFileIO.IsTemporary()) {
|
||||||
project.SetProjectName(wxFileName(fileName).GetName());
|
wxFileName fn(fileName);
|
||||||
|
project.SetProjectName(fn.GetName());
|
||||||
|
project.SetInitialImportPath(fn.GetPath());
|
||||||
projectFileIO.SetProjectTitle();
|
projectFileIO.SetProjectTitle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -92,7 +92,9 @@ public:
|
||||||
void OpenFile(const FilePath &fileName, bool addtohistory = true);
|
void OpenFile(const FilePath &fileName, bool addtohistory = true);
|
||||||
|
|
||||||
// If pNewTrackList is passed in non-NULL, it gets filled with the pointers to NEW tracks.
|
// If pNewTrackList is passed in non-NULL, it gets filled with the pointers to NEW tracks.
|
||||||
bool Import(const FilePath &fileName, WaveTrackArray *pTrackArray = NULL);
|
bool Import(const FilePath &fileName,
|
||||||
|
WaveTrackArray *pTrackArray = nullptr,
|
||||||
|
bool addToHistory = true);
|
||||||
|
|
||||||
// Takes array of unique pointers; returns array of shared
|
// Takes array of unique pointers; returns array of shared
|
||||||
std::vector< std::shared_ptr<Track> >
|
std::vector< std::shared_ptr<Track> >
|
||||||
|
|
|
@ -48,7 +48,7 @@ void ImportCommand::PopulateOrExchange(ShuttleGui & S)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImportCommand::Apply(const CommandContext & context){
|
bool ImportCommand::Apply(const CommandContext & context){
|
||||||
return ProjectFileManager::Get( context.project ).Import(mFileName);
|
return ProjectFileManager::Get( context.project ).Import(mFileName, nullptr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -466,6 +466,14 @@ bool Exporter::Process(bool selectedOnly, double t0, double t1)
|
||||||
// Get rid of mixerspec
|
// Get rid of mixerspec
|
||||||
mMixerSpec.reset();
|
mMixerSpec.reset();
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
if (mFormatName.empty()) {
|
||||||
|
gPrefs->Write(wxT("/Export/Format"), mPlugins[mFormat]->GetFormat(mSubFormat));
|
||||||
|
}
|
||||||
|
|
||||||
|
FileNames::UpdateDefaultPath(FileNames::Operation::Export, mFilename.GetPath());
|
||||||
|
}
|
||||||
|
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -774,11 +782,6 @@ bool Exporter::GetFilename()
|
||||||
//
|
//
|
||||||
bool Exporter::CheckFilename()
|
bool Exporter::CheckFilename()
|
||||||
{
|
{
|
||||||
if( mFormatName.empty() )
|
|
||||||
gPrefs->Write(wxT("/Export/Format"), mPlugins[mFormat]->GetFormat(mSubFormat));
|
|
||||||
|
|
||||||
FileNames::UpdateDefaultPath(FileNames::Operation::Export, mFilename.GetPath());
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// To be even safer, return a temporary file name based
|
// To be even safer, return a temporary file name based
|
||||||
// on this one...
|
// on this one...
|
||||||
|
@ -924,6 +927,8 @@ bool Exporter::ExportTracks()
|
||||||
::wxRemoveFile(mActualName.GetFullPath());
|
::wxRemoveFile(mActualName.GetFullPath());
|
||||||
::wxRenameFile(mFilename.GetFullPath(), mActualName.GetFullPath());
|
::wxRenameFile(mFilename.GetFullPath(), mActualName.GetFullPath());
|
||||||
}
|
}
|
||||||
|
// Restore filname
|
||||||
|
mFilename = mActualName;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if ( ! success )
|
if ( ! success )
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "../WaveTrack.h"
|
#include "../WaveTrack.h"
|
||||||
#include "../commands/CommandContext.h"
|
#include "../commands/CommandContext.h"
|
||||||
#include "../commands/CommandManager.h"
|
#include "../commands/CommandManager.h"
|
||||||
|
#include "../export/ExportMP3.h"
|
||||||
#include "../export/ExportMultiple.h"
|
#include "../export/ExportMultiple.h"
|
||||||
#include "../import/Import.h"
|
#include "../import/Import.h"
|
||||||
#include "../import/ImportMIDI.h"
|
#include "../import/ImportMIDI.h"
|
||||||
|
@ -40,7 +41,10 @@
|
||||||
|
|
||||||
// private helper classes and functions
|
// private helper classes and functions
|
||||||
namespace {
|
namespace {
|
||||||
void DoExport( AudacityProject &project, const FileExtension & Format )
|
|
||||||
|
void DoExport(AudacityProject &project,
|
||||||
|
const FileExtension &format,
|
||||||
|
const wxString &prefix = {})
|
||||||
{
|
{
|
||||||
auto &tracks = TrackList::Get( project );
|
auto &tracks = TrackList::Get( project );
|
||||||
auto &projectFileIO = ProjectFileIO::Get( project );
|
auto &projectFileIO = ProjectFileIO::Get( project );
|
||||||
|
@ -49,65 +53,83 @@ void DoExport( AudacityProject &project, const FileExtension & Format )
|
||||||
|
|
||||||
double t0 = 0.0;
|
double t0 = 0.0;
|
||||||
double t1 = tracks.GetEndTime();
|
double t1 = tracks.GetEndTime();
|
||||||
|
wxString projectName = project.GetProjectName();
|
||||||
|
|
||||||
// Prompt for file name and/or extension?
|
// Prompt for file name and/or extension?
|
||||||
bool bPromptingRequired =
|
bool bPromptingRequired = !project.mBatchMode ||
|
||||||
(project.mBatchMode == 0) || projectFileIO.GetFileName().empty() ||
|
projectName.empty() ||
|
||||||
Format.empty();
|
format.empty();
|
||||||
wxString filename;
|
|
||||||
|
|
||||||
if (!bPromptingRequired) {
|
bool success = false;
|
||||||
|
if (bPromptingRequired) {
|
||||||
// We're in batch mode, and we have an mFileName and Format.
|
|
||||||
wxString extension = Format;
|
|
||||||
extension.MakeLower();
|
|
||||||
|
|
||||||
filename =
|
|
||||||
MacroCommands::BuildCleanFileName(projectFileIO.GetFileName(), extension);
|
|
||||||
|
|
||||||
// Bug 1854, No warning of file overwrite
|
|
||||||
// (when export is called from Macros).
|
|
||||||
int counter = 0;
|
|
||||||
bPromptingRequired = wxFileExists(filename);
|
|
||||||
|
|
||||||
// We'll try alternative names to avoid overwriting.
|
|
||||||
while ( bPromptingRequired && counter < 100 ) {
|
|
||||||
counter++;
|
|
||||||
wxString number;
|
|
||||||
number.Printf("%03i", counter);
|
|
||||||
// So now the name has a number in it too.
|
|
||||||
filename = MacroCommands::BuildCleanFileName(
|
|
||||||
projectFileIO.GetFileName() + number, extension);
|
|
||||||
bPromptingRequired = wxFileExists(filename);
|
|
||||||
}
|
|
||||||
// If we've run out of alternative names, we will fall back to prompting
|
|
||||||
// - even if in a macro.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (bPromptingRequired)
|
|
||||||
{
|
|
||||||
// Do export with prompting.
|
// Do export with prompting.
|
||||||
e.SetDefaultFormat(Format);
|
e.SetDefaultFormat(format);
|
||||||
e.Process(false, t0, t1);
|
success = e.Process(false, t0, t1);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
// If we've gotten to this point, we are in batch mode, have a file format,
|
||||||
FileHistory::Global().Append(filename);
|
// and the project has either been saved or a file has been imported. So, we
|
||||||
|
// want to use the project's path if it has been saved, otherwise use the
|
||||||
|
// initial import path.
|
||||||
|
FilePath pathName = !projectFileIO.IsTemporary() ?
|
||||||
|
wxPathOnly(projectFileIO.GetFileName()) :
|
||||||
|
project.GetInitialImportPath();
|
||||||
|
wxFileName fileName(pathName,
|
||||||
|
prefix + projectName,
|
||||||
|
format.Lower());
|
||||||
|
|
||||||
|
// Append the "macro-output" directory to the path
|
||||||
|
const wxString macroDir( "macro-output" );
|
||||||
|
if (fileName.GetDirs().back() != macroDir) {
|
||||||
|
fileName.AppendDir(macroDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
wxString justName = fileName.GetName();
|
||||||
|
wxString extension = fileName.GetExt();
|
||||||
|
FilePath fullPath = fileName.GetFullPath();
|
||||||
|
|
||||||
|
if (wxFileName::FileExists(fileName.GetPath())) {
|
||||||
|
AudacityMessageBox(
|
||||||
|
XO("Cannot create directory '%s'. \n"
|
||||||
|
"File already exists that is not a directory"),
|
||||||
|
Verbatim(fullPath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileName.Mkdir(0777, wxPATH_MKDIR_FULL); // make sure it exists
|
||||||
|
|
||||||
|
int nChannels = (tracks.Any() - &Track::IsLeader ).empty() ? 1 : 2;
|
||||||
|
|
||||||
// We're in batch mode, the file does not exist already.
|
// We're in batch mode, the file does not exist already.
|
||||||
// We really can proceed without prompting.
|
// We really can proceed without prompting.
|
||||||
int nChannels = MacroCommands::IsMono( &project ) ? 1 : 2;
|
success = e.Process(
|
||||||
e.Process(
|
|
||||||
nChannels, // numChannels,
|
nChannels, // numChannels,
|
||||||
Format, // type,
|
format, // type,
|
||||||
filename, // filename,
|
fullPath, // full path,
|
||||||
false, // selectedOnly,
|
false, // selectedOnly,
|
||||||
t0, // t0
|
t0, // t0
|
||||||
t1 // t1
|
t1 // t1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (success && !project.mBatchMode) {
|
||||||
|
FileHistory::Global().Append(e.GetAutoExportFileName().GetFullPath());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WriteMp3File(AudacityProject &project, const wxString &prefix)
|
||||||
|
{
|
||||||
|
long prevBitRate = gPrefs->Read(wxT("/FileFormats/MP3Bitrate"), 128);
|
||||||
|
gPrefs->Write(wxT("/FileFormats/MP3Bitrate"), 56);
|
||||||
|
auto prevMode = MP3RateModeSetting.ReadEnum();
|
||||||
|
MP3RateModeSetting.WriteEnum(MODE_CBR);
|
||||||
|
|
||||||
|
DoExport(project, wxT("MP3"), prefix);
|
||||||
|
|
||||||
|
gPrefs->Write(wxT("/FileFormats/MP3Bitrate"), prevBitRate);
|
||||||
|
MP3RateModeSetting.WriteEnum(prevMode);
|
||||||
|
gPrefs->Flush();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compact dialog
|
// Compact dialog
|
||||||
|
@ -629,6 +651,21 @@ void OnExit(const CommandContext &WXUNUSED(context) )
|
||||||
wxTheApp->AddPendingEvent( evt );
|
wxTheApp->AddPendingEvent( evt );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OnExportMP3_56k_before(const CommandContext &context)
|
||||||
|
{
|
||||||
|
WriteMp3File(context.project, wxT("MasterBefore_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnExportMP3_56k_after(const CommandContext &context)
|
||||||
|
{
|
||||||
|
WriteMp3File(context.project, wxT("MasterAfter_"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnExportFLAC(const CommandContext &context)
|
||||||
|
{
|
||||||
|
DoExport(context.project, "FLAC");
|
||||||
|
}
|
||||||
|
|
||||||
}; // struct Handler
|
}; // struct Handler
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -798,6 +835,42 @@ AttachedItem sAttachment1{
|
||||||
wxT(""),
|
wxT(""),
|
||||||
Shared( FileMenu() )
|
Shared( FileMenu() )
|
||||||
};
|
};
|
||||||
|
|
||||||
|
BaseItemSharedPtr HiddenFileMenu()
|
||||||
|
{
|
||||||
|
static BaseItemSharedPtr menu
|
||||||
|
{
|
||||||
|
(
|
||||||
|
FinderScope{ findCommandHandler },
|
||||||
|
ConditionalItems( wxT("HiddenFileItems"),
|
||||||
|
[]()
|
||||||
|
{
|
||||||
|
// Ensures that these items never appear in a menu, but
|
||||||
|
// are still available to scripting
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
Menu( wxT("HiddenFileMenu"), XXO("Hidden File Menu"),
|
||||||
|
Command( wxT("ExportMP3_56k_before"), XXO("Export as MP3 56k before"),
|
||||||
|
FN(OnExportMP3_56k_before),
|
||||||
|
AudioIONotBusyFlag() ),
|
||||||
|
Command( wxT("ExportMP3_56k_after"), XXO("Export as MP3 56k after"),
|
||||||
|
FN(OnExportMP3_56k_after),
|
||||||
|
AudioIONotBusyFlag() ),
|
||||||
|
Command( wxT("ExportFLAC"), XXO("Export as FLAC"),
|
||||||
|
FN(OnExportFLAC),
|
||||||
|
AudioIONotBusyFlag() )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
};
|
||||||
|
return menu;
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachedItem sAttachment2{
|
||||||
|
wxT(""),
|
||||||
|
Shared( HiddenFileMenu() )
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef FN
|
#undef FN
|
||||||
|
|
Loading…
Reference in New Issue