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:
Leland Lucius 2020-08-10 17:48:59 -05:00
parent 5c719e8d65
commit 9d749fef14
9 changed files with 171 additions and 371 deletions

View File

@ -25,7 +25,6 @@ processing. See also MacrosWindow and ApplyMacroDialog.
#include "Project.h"
#include "ProjectAudioManager.h"
#include "ProjectFileIO.h"
#include "ProjectHistory.h"
#include "ProjectSettings.h"
#include "ProjectWindow.h"
@ -39,7 +38,6 @@ processing. See also MacrosWindow and ApplyMacroDialog.
#include "SelectUtilities.h"
#include "Shuttle.h"
#include "Track.h"
#include "export/ExportMP3.h"
#include "AllThemeResources.h"
@ -47,46 +45,6 @@ processing. See also MacrosWindow and ApplyMacroDialog.
#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 )
: mProject{ project }
, mExporter{ project }
@ -108,16 +66,12 @@ MacroCommands::MacroCommands( AudacityProject &project )
static const auto MP3Conversion = XO("MP3 Conversion");
static const auto FadeEnds = XO("Fade Ends");
static const auto SelectToEnds = XO("Select to Ends");
wxArrayStringEx MacroCommands::GetNamesOfDefaultMacros()
{
return {
MP3Conversion.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("FadeOut") );
AddToMacro( wxT("Select"), wxT("Start=\"0\" End=\"0\"") );
} else if (name == SelectToEnds.Translation() ){
AddToMacro( wxT("SelCursorEnd") );
AddToMacro( wxT("SelStartCursor") );
}
}
}
CommandID MacroCommands::GetCommand(int index)
@ -348,15 +299,7 @@ MacroCommandsCatalog::MacroCommandsCatalog( const AudacityProject *project )
if (!project)
return;
// CLEANSPEECH remnant
Entries commands;
for( const auto &command : SpecialCommands )
commands.push_back( {
{ command.second, command.first },
XO("Special Command")
} );
// end CLEANSPEECH remnant
PluginManager & pm = PluginManager::Get();
EffectManager & em = EffectManager::Get();
@ -440,8 +383,7 @@ MacroCommandsCatalog::MacroCommandsCatalog( const AudacityProject *project )
// Sort commands by their user-visible names.
// 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,
// keeping specials before effects and menu items, and lastly commands.
// I'm not sure, but at least I can sort stably for a better defined result.
auto less =
[](const Entry &a, const Entry &b)
{ return a.name.StrippedTranslation() <
@ -486,8 +428,6 @@ auto MacroCommandsCatalog::ByCommandId( const CommandID &commandId ) const
{ return entry.name.Internal() == commandId; });
}
wxString MacroCommands::GetCurrentParamsFor(const CommandID & command)
{
const PluginID & ID =
@ -546,238 +486,6 @@ wxString MacroCommands::PromptForPresetFor(const CommandID & command, const wxSt
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.
///
/// 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,
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.
const PluginID & ID =
EffectManager::Get().GetEffectByIdentifier( command );

View File

@ -72,9 +72,6 @@ class MacroCommands final {
bool ApplyCommandInBatchMode( const TranslatableString &friendlyCommand,
const CommandID & command, const wxString &params,
CommandContext const * pContext = NULL);
bool ApplySpecialCommand(
int iCommand, const TranslatableString &friendlyCommand,
const CommandID & command, const wxString & params);
bool ApplyEffectCommand(
const PluginID & ID, const TranslatableString &friendlyCommand,
const CommandID & command,
@ -82,13 +79,6 @@ class MacroCommands final {
bool ReportAndSkip( const TranslatableString & friendlyCommand, const wxString & params );
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.
static void MigrateLegacyChains();
static wxArrayString GetNames();

View File

@ -148,6 +148,19 @@ void AudacityProject::SetProjectName(const wxString &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 )
{
auto ptr = project.GetFrame();

View File

@ -138,7 +138,12 @@ class AUDACITY_DLL_API AudacityProject final
const wxString &GetProjectName() const;
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
wxString mName;
@ -146,6 +151,8 @@ class AUDACITY_DLL_API AudacityProject final
static int mProjectCounter;// global counter.
int mProjectNo; // count when this project was created.
FilePath mInitialImportPath;
public:
bool mbBusyImporting{ false }; // used to fix bug 584
int mBatchMode{ 0 };// 0 means not, >0 means in batch mode.

View File

@ -1116,6 +1116,7 @@ ProjectFileManager::AddImportedTracks(const FilePath &fileName,
// the filename to the just imported path.
if (initiallyEmpty && projectFileIO.IsTemporary()) {
project.SetProjectName(fn.GetName());
project.SetInitialImportPath(fn.GetPath());
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.
bool ProjectFileManager::Import(
const FilePath &fileName, WaveTrackArray* pTrackArray /*= NULL*/)
const FilePath &fileName,
WaveTrackArray *pTrackArray /* = nullptr */,
bool addToHistory /* = true */)
{
auto &project = mProject;
auto &projectFileIO = ProjectFileIO::Get(project);
@ -1145,13 +1148,17 @@ bool ProjectFileManager::Import(
// If the project was clean and temporary (not permanently saved), then set
// the filename to the just imported path.
if (initiallyEmpty && projectFileIO.IsTemporary()) {
project.SetProjectName(wxFileName(fileName).GetName());
wxFileName fn(fileName);
project.SetProjectName(fn.GetName());
project.SetInitialImportPath(fn.GetPath());
projectFileIO.SetProjectTitle();
}
history.PushState(XO("Imported '%s'").Format(fileName), XO("Import"));
FileHistory::Global().Append(fileName);
if (addToHistory) {
FileHistory::Global().Append(fileName);
}
}
return false;
@ -1182,7 +1189,9 @@ bool ProjectFileManager::Import(
if (!success)
return false;
FileHistory::Global().Append(fileName);
if (addToHistory) {
FileHistory::Global().Append(fileName);
}
// no more errors, commit
committed = true;
@ -1204,7 +1213,9 @@ bool ProjectFileManager::Import(
// If the project was clean and temporary (not permanently saved), then set
// the filename to the just imported path.
if (initiallyEmpty && projectFileIO.IsTemporary()) {
project.SetProjectName(wxFileName(fileName).GetName());
wxFileName fn(fileName);
project.SetProjectName(fn.GetName());
project.SetInitialImportPath(fn.GetPath());
projectFileIO.SetProjectTitle();
}

View File

@ -92,7 +92,9 @@ public:
void OpenFile(const FilePath &fileName, bool addtohistory = true);
// 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
std::vector< std::shared_ptr<Track> >

View File

@ -48,7 +48,7 @@ void ImportCommand::PopulateOrExchange(ShuttleGui & S)
}
bool ImportCommand::Apply(const CommandContext & context){
return ProjectFileManager::Get( context.project ).Import(mFileName);
return ProjectFileManager::Get( context.project ).Import(mFileName, nullptr, false);
}

View File

@ -466,6 +466,14 @@ bool Exporter::Process(bool selectedOnly, double t0, double t1)
// Get rid of mixerspec
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;
}
@ -774,11 +782,6 @@ bool Exporter::GetFilename()
//
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
// on this one...
@ -924,6 +927,8 @@ bool Exporter::ExportTracks()
::wxRemoveFile(mActualName.GetFullPath());
::wxRenameFile(mFilename.GetFullPath(), mActualName.GetFullPath());
}
// Restore filname
mFilename = mActualName;
}
else {
if ( ! success )

View File

@ -23,6 +23,7 @@
#include "../WaveTrack.h"
#include "../commands/CommandContext.h"
#include "../commands/CommandManager.h"
#include "../export/ExportMP3.h"
#include "../export/ExportMultiple.h"
#include "../import/Import.h"
#include "../import/ImportMIDI.h"
@ -40,7 +41,10 @@
// private helper classes and functions
namespace {
void DoExport( AudacityProject &project, const FileExtension & Format )
void DoExport(AudacityProject &project,
const FileExtension &format,
const wxString &prefix = {})
{
auto &tracks = TrackList::Get( project );
auto &projectFileIO = ProjectFileIO::Get( project );
@ -49,65 +53,83 @@ void DoExport( AudacityProject &project, const FileExtension & Format )
double t0 = 0.0;
double t1 = tracks.GetEndTime();
wxString projectName = project.GetProjectName();
// Prompt for file name and/or extension?
bool bPromptingRequired =
(project.mBatchMode == 0) || projectFileIO.GetFileName().empty() ||
Format.empty();
wxString filename;
bool bPromptingRequired = !project.mBatchMode ||
projectName.empty() ||
format.empty();
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)
{
bool success = false;
if (bPromptingRequired) {
// Do export with prompting.
e.SetDefaultFormat(Format);
e.Process(false, t0, t1);
e.SetDefaultFormat(format);
success = e.Process(false, t0, t1);
}
else
{
FileHistory::Global().Append(filename);
else {
// If we've gotten to this point, we are in batch mode, have a file format,
// 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 really can proceed without prompting.
int nChannels = MacroCommands::IsMono( &project ) ? 1 : 2;
e.Process(
success = e.Process(
nChannels, // numChannels,
Format, // type,
filename, // filename,
format, // type,
fullPath, // full path,
false, // selectedOnly,
t0, // t0
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
@ -629,6 +651,21 @@ void OnExit(const CommandContext &WXUNUSED(context) )
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
} // namespace
@ -798,6 +835,42 @@ AttachedItem sAttachment1{
wxT(""),
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