1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-20 14:20:06 +02:00

Timer Recording Enhancements to allow Automatic Save and Export after

recording.  Also allows for additional options to be carried out after a
successful timer recording such as Exit, Retsrat and Shutdown.
This commit is contained in:
tip2tail 2016-02-25 13:47:48 +00:00
parent 484f23dcc7
commit aa5ffe99a7
9 changed files with 1005 additions and 93 deletions

View File

@ -144,6 +144,18 @@ enum {
kAlignTogether
};
// Post Timer Recording Actions
// Ensure this matches the enum in TimerRecordDialog.cpp
enum {
POST_TIMER_RECORD_STOPPED = -3,
POST_TIMER_RECORD_CANCEL_WAIT,
POST_TIMER_RECORD_CANCEL,
POST_TIMER_RECORD_NOTHING,
POST_TIMER_RECORD_CLOSE,
POST_TIMER_RECORD_RESTART,
POST_TIMER_RECORD_SHUTDOWN
};
// Define functor subclasses that dispatch to the correct call sequence on
// member functions of AudacityProject
@ -6309,24 +6321,80 @@ void AudacityProject::OnNewTimeTrack()
void AudacityProject::OnTimerRecord()
{
// MY: Due to improvements in how Timer Recording saves and/or exports
// it is now safer to disable Timer Recording when there is more than
// one open project.
if (GetOpenProjectCount() > 1) {
wxMessageBox(_("Timer Recording cannot be used with more than one open project.\n\n\
Please close any additional projects and try again."),
_("Timer Recording"),
wxICON_INFORMATION | wxOK);
return;
}
// MY: If the project has unsaved changes then we no longer allow access
// to Timer Recording. This decision has been taken as the safest approach
// preventing issues surrounding "dirty" projects when Automatic Save/Export
// is used in Timer Recording.
if (GetUndoManager()->UnsavedChanges()) {
wxMessageBox(_("Timer Recording cannot be used while you have unsaved changes.\n\n\
Please save or close this project and try again."),
_("Timer Recording"),
wxICON_INFORMATION | wxOK);
return;
}
// We use this variable to display "Current Project" in the Timer Recording save project field
bool bProjectSaved = IsProjectSaved();
//we break the prompting and waiting dialogs into two sections
//because they both give the user a chance to click cancel
//and therefore remove the newly inserted track.
TimerRecordDialog dialog(this /* parent */ );
TimerRecordDialog dialog(this, bProjectSaved); /* parent, project saved? */
int modalResult = dialog.ShowModal();
if (modalResult == wxID_CANCEL)
{
// Cancelled before recording - don't need to do anyting.
}
else if(!dialog.RunWaitDialog())
else
{
//RunWaitDialog() shows the "wait for start" as well as "recording" dialog
//if it returned false it means the user cancelled while the recording, so throw out the fresh track.
//However, we can't undo it here because the PushState() is called in TrackPanel::OnTimer(),
//which is blocked by this function.
//so instead we mark a flag to undo it there.
mTimerRecordCanceled = true;
int iTimerRecordingOutcome = dialog.RunWaitDialog();
switch (iTimerRecordingOutcome) {
case POST_TIMER_RECORD_CANCEL_WAIT:
// Canceled on the wait dialog
if (GetUndoManager()->UndoAvailable()) {
// MY: We need to roll back what changes we have made here
OnUndo();
}
break;
case POST_TIMER_RECORD_CANCEL:
// RunWaitDialog() shows the "wait for start" as well as "recording" dialog
// if it returned POST_TIMER_RECORD_CANCEL it means the user cancelled while the recording, so throw out the fresh track.
// However, we can't undo it here because the PushState() is called in TrackPanel::OnTimer(),
// which is blocked by this function.
// so instead we mark a flag to undo it there.
mTimerRecordCanceled = true;
break;
case POST_TIMER_RECORD_NOTHING:
// No action required
break;
case POST_TIMER_RECORD_CLOSE:
// Quit Audacity
exit(0);
break;
case POST_TIMER_RECORD_RESTART:
// Restart System
#ifdef __WINDOWS__
system("shutdown /r /f /t 30");
#endif
break;
case POST_TIMER_RECORD_SHUTDOWN:
// Shutdown System
#ifdef __WINDOWS__
system("shutdown /s /f /t 30");
#endif
break;
}
}
}

View File

@ -5193,3 +5193,57 @@ void AudacityProject::ReleaseKeyboard(wxWindow * /* handler */)
return;
}
bool AudacityProject::ExportFromTimerRecording(wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex)
{
Exporter e;
wxGetApp().SetMissingAliasedFileWarningShouldShow(true);
return e.ProcessFromTimerRecording(this, false, 0.0, mTracks->GetEndTime(), fnFile, iFormat, iSubFormat, iFilterIndex);
}
int AudacityProject::GetOpenProjectCount() {
return gAudacityProjects.Count();
}
bool AudacityProject::IsProjectSaved() {
wxString sProjectName = mDirManager->GetProjectName();
return (sProjectName != wxT(""));
}
bool AudacityProject::SaveFromTimerRecording(wxFileName fnFile) {
// MY: Will save the project to a new location a-la Save As
// and then tidy up after itself.
wxString sNewFileName = fnFile.GetFullPath();
// MY: To allow SaveAs from Timer Recording we need to check what
// the value of mFileName is befoer we change it.
wxString sOldFilename = "";
if (IsProjectSaved()) {
sOldFilename = mFileName;
}
// MY: If the project file already exists then bail out
// and send populate the message string (pointer) so
// we can tell the user what went wrong.
if (wxFileExists(sNewFileName)) {
return false;
}
mFileName = sNewFileName;
SetProjectTitle();
bool bSuccess = Save(false, true, false);
if (bSuccess) {
wxGetApp().AddFileToHistory(mFileName);
} else
{
// Reset file name on error
mFileName = sOldFilename;
SetProjectTitle();
}
return bSuccess;
}

View File

@ -275,6 +275,12 @@ class AUDACITY_DLL_API AudacityProject final : public wxFrame,
*/
wxDialog *GetMissingAliasFileDialog();
// Timer Record Auto Save/Export Routines
bool SaveFromTimerRecording(wxFileName fnFile);
bool ExportFromTimerRecording(wxFileName fnFile, int iFormat, int iSubFormat, int iFilterIndex);
int GetOpenProjectCount();
bool IsProjectSaved();
#include "Menus.h"
CommandManager *GetCommandManager() { return &mCommandManager; }

View File

@ -487,7 +487,6 @@ wxSpinCtrl * ShuttleGuiBase::AddSpinCtrl(const wxString &Prompt, int Value, int
return pSpinCtrl;
}
wxTextCtrl * ShuttleGuiBase::AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
{
UseUpId();

View File

@ -70,7 +70,6 @@ class Shuttle;
class WrappedType;
class AUDACITY_DLL_API ShuttleGuiBase /* not final */
{
public:

View File

@ -20,6 +20,7 @@
#include "Audacity.h"
#include "TimerRecordDialog.h"
#include "FileNames.h"
#include <wx/defs.h>
#include <wx/datetime.h>
@ -44,7 +45,30 @@ enum { // control IDs
ID_TIMETEXT_START,
ID_DATEPICKER_END,
ID_TIMETEXT_END,
ID_TIMETEXT_DURATION
ID_TIMETEXT_DURATION,
ID_AUTOSAVEPATH_BUTTON,
ID_AUTOSAVEPATH_TEXT,
ID_AUTOEXPORTPATH_BUTTON,
ID_AUTOEXPORTPATH_TEXT,
ID_AUTOSAVE_CHECKBOX,
ID_AUTOEXPORT_CHECKBOX
};
enum {
CONTROL_GROUP_SAVE,
CONTROL_GROUP_EXPORT
};
// Post Timer Recording Actions
// Ensure this matches the enum in Menus.cpp
enum {
POST_TIMER_RECORD_STOPPED = -3,
POST_TIMER_RECORD_CANCEL_WAIT,
POST_TIMER_RECORD_CANCEL,
POST_TIMER_RECORD_NOTHING,
POST_TIMER_RECORD_CLOSE,
POST_TIMER_RECORD_RESTART,
POST_TIMER_RECORD_SHUTDOWN
};
const int kTimerInterval = 50; // ms
@ -66,9 +90,16 @@ BEGIN_EVENT_TABLE(TimerRecordDialog, wxDialog)
EVT_BUTTON(wxID_OK, TimerRecordDialog::OnOK)
EVT_TIMER(TIMER_ID, TimerRecordDialog::OnTimer)
EVT_BUTTON(ID_AUTOSAVEPATH_BUTTON, TimerRecordDialog::OnAutoSavePathButton_Click)
EVT_BUTTON(ID_AUTOEXPORTPATH_BUTTON, TimerRecordDialog::OnAutoExportPathButton_Click)
EVT_CHECKBOX(ID_AUTOSAVE_CHECKBOX, TimerRecordDialog::OnAutoSaveCheckBox_Change)
EVT_CHECKBOX(ID_AUTOEXPORT_CHECKBOX, TimerRecordDialog::OnAutoExportCheckBox_Change)
END_EVENT_TABLE()
TimerRecordDialog::TimerRecordDialog(wxWindow* parent)
TimerRecordDialog::TimerRecordDialog(wxWindow* parent, bool bAlreadySaved)
: wxDialog(parent, -1, _("Audacity Timer Record"), wxDefaultPosition,
wxDefaultSize, wxCAPTION)
{
@ -88,6 +119,9 @@ TimerRecordDialog::TimerRecordDialog(wxWindow* parent)
m_pTimeTextCtrl_Duration = NULL;
// Do we allow the user to change the Automatic Save file?
m_bProjectAlreadySaved = bAlreadySaved;
ShuttleGui S(this, eIsCreating);
this->PopulateOrExchange(S);
@ -98,6 +132,10 @@ TimerRecordDialog::TimerRecordDialog(wxWindow* parent)
m_timer.SetOwner(this, TIMER_ID);
m_timer.Start(kTimerInterval);
// Do we need to tidy up when the timer recording has been completed?
m_bProjectCleanupRequired = !(this->HaveFilesToRecover());
}
TimerRecordDialog::~TimerRecordDialog()
@ -210,6 +248,66 @@ void TimerRecordDialog::OnTimeText_Duration(wxCommandEvent& WXUNUSED(event))
this->UpdateEnd(); // Keep Start constant and update End for changed Duration.
}
// New events for timer recording automation
void TimerRecordDialog::OnAutoSavePathButton_Click(wxCommandEvent& WXUNUSED(event))
{
wxString fName = FileSelector(_T("Save Timer Recording As"),
m_fnAutoSaveFile.GetPath(),
m_fnAutoSaveFile.GetFullName(),
wxT("aup"),
_("Audacity projects") + wxT(" (*.aup)|*.aup"),
wxFD_SAVE | wxRESIZE_BORDER,
this);
if (fName == wxT(""))
return;
// MY: If project already exits then abort - we do not allow users to overwrite an existing project
if (wxFileExists(fName)) {
wxMessageDialog m(
NULL,
_("The selected file name could not be used\nfor Timer Recording because it would overwrite another project.\n\
Please try again and select an original name."),
_("Error Saving Timer Recording Project"),
wxOK|wxICON_ERROR);
m.ShowModal();
return;
}
// MY: Set this boolean to false so we now do a SaveAs at the end of the recording
m_bProjectAlreadySaved = false;
m_fnAutoSaveFile = fName;
m_fnAutoSaveFile.SetExt(wxT("aup"));
this->UpdateTextBoxControls();
}
void TimerRecordDialog::OnAutoExportPathButton_Click(wxCommandEvent& WXUNUSED(event))
{
AudacityProject* pProject = GetActiveProject();
Exporter eExporter;
// Call the Exporter to set the options required
if (eExporter.SetAutoExportOptions(pProject)) {
// Populate the options so that we can destroy this instance of the Exporter
m_fnAutoExportFile = eExporter.GetAutoExportFileName();
m_iAutoExportFormat = eExporter.GetAutoExportFormat();
m_iAutoExportSubFormat = eExporter.GetAutoExportSubFormat();
m_iAutoExportFilterIndex = eExporter.GetAutoExportFilterIndex();
// Update the text controls
this->UpdateTextBoxControls();
}
}
void TimerRecordDialog::OnAutoSaveCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
EnableDisableAutoControls(m_pTimerAutoSaveCheckBoxCtrl->GetValue(), CONTROL_GROUP_SAVE);
}
void TimerRecordDialog::OnAutoExportCheckBox_Change(wxCommandEvent& WXUNUSED(event)) {
EnableDisableAutoControls(m_pTimerAutoExportCheckBoxCtrl->GetValue(), CONTROL_GROUP_EXPORT);
}
void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
{
this->TransferDataFromWindow();
@ -220,6 +318,24 @@ void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
return;
}
// Validate that we have a Save and/or Export path setup if the appropriate check box is ticked
wxString sTemp = m_fnAutoSaveFile.GetFullPath();
if (m_pTimerAutoSaveCheckBoxCtrl->IsChecked()) {
if (!m_fnAutoSaveFile.IsOk() || m_fnAutoSaveFile.IsDir()) {
wxMessageBox(_("Automatic Save path is invalid."),
_("Error in Automatic Save"), wxICON_EXCLAMATION | wxOK);
return;
}
}
if (m_pTimerAutoExportCheckBoxCtrl->IsChecked()) {
if (!m_fnAutoExportFile.IsOk() || m_fnAutoExportFile.IsDir()) {
wxMessageBox(_("Automatic Export path is invalid."),
_("Error in Automatic Export"), wxICON_EXCLAMATION | wxOK);
return;
}
}
m_timer.Stop(); // Don't need to keep updating m_DateTime_Start to prevent backdating.
this->EndModal(wxID_OK);
wxLongLong duration = m_TimeSpan_Duration.GetSeconds();
@ -228,12 +344,95 @@ void TimerRecordDialog::OnOK(wxCommandEvent& WXUNUSED(event))
gPrefs->Flush();
}
///Runs the wait for start dialog. Returns false if the user clicks stop while we are recording
///so that the high
// <ANSWER-ME: so that the "high" what does what?>
bool TimerRecordDialog::RunWaitDialog()
void TimerRecordDialog::EnableDisableAutoControls(bool bEnable, int iControlGoup) {
if (iControlGoup == CONTROL_GROUP_EXPORT) {
// Enable or Disable the Export controls
if (bEnable) {
m_pTimerExportPathTextCtrl->Enable();
m_pTimerExportPathButtonCtrl->Enable();
}
else {
m_pTimerExportPathTextCtrl->Disable();
m_pTimerExportPathButtonCtrl->Disable();
}
}
else if (iControlGoup == CONTROL_GROUP_SAVE) {
// Enable or Disable the Save controls
if (bEnable) {
m_pTimerSavePathTextCtrl->Enable();
m_pTimerSavePathButtonCtrl->Enable();
}
else {
m_pTimerSavePathTextCtrl->Disable();
m_pTimerSavePathButtonCtrl->Disable();
}
}
// Enable or disable the Choice box - if there is no Save or Export then this will be disabled
if (m_pTimerAutoSaveCheckBoxCtrl->GetValue() || m_pTimerAutoExportCheckBoxCtrl->GetValue()) {
m_pTimerAfterCompleteChoiceCtrl->Enable();
}
else {
m_pTimerAfterCompleteChoiceCtrl->SetSelection(POST_TIMER_RECORD_NOTHING);
m_pTimerAfterCompleteChoiceCtrl->Disable();
}
}
void TimerRecordDialog::UpdateTextBoxControls() {
// Will update the text box controls
m_pTimerSavePathTextCtrl->SetValue(m_fnAutoSaveFile.GetFullPath());
m_pTimerExportPathTextCtrl->SetValue(m_fnAutoExportFile.GetFullPath());
// MY: Ensure we still display "Current Project" if this has already been saved
if (m_bProjectAlreadySaved) {
m_pTimerSavePathTextCtrl->SetValue(_("Current Project"));
}
}
// Copied from AutoRecovery.cpp - for use with Timer Recording Improvements
bool TimerRecordDialog::HaveFilesToRecover()
{
wxDir dir(FileNames::AutoSaveDir());
if (!dir.IsOpened())
{
wxMessageBox(_("Could not enumerate files in auto save directory."),
_("Error"), wxICON_STOP);
return false;
}
wxString filename;
bool c = dir.GetFirst(&filename, wxT("*.autosave"), wxDIR_FILES);
return c;
}
bool TimerRecordDialog::RemoveAllAutoSaveFiles()
{
wxArrayString files;
wxDir::GetAllFiles(FileNames::AutoSaveDir(), &files,
wxT("*.autosave"), wxDIR_FILES);
for (unsigned int i = 0; i < files.GetCount(); i++)
{
if (!wxRemoveFile(files[i]))
{
// I don't think this error message is actually useful.
// -dmazzoni
//wxMessageBox(wxT("Could not remove auto save file: " + files[i]),
// _("Error"), wxICON_STOP);
return false;
}
}
return true;
}
/// Runs the wait for start dialog. Returns -1 if the user clicks stop while we are recording
/// or if the post recording actions fail.
int TimerRecordDialog::RunWaitDialog()
{
AudacityProject* pProject = GetActiveProject();
int updateResult = eProgressSuccess;
if (m_DateTime_Start > wxDateTime::UNow())
@ -242,7 +441,7 @@ bool TimerRecordDialog::RunWaitDialog()
if (updateResult != eProgressSuccess)
{
// Don't proceed, but don't treat it as canceled recording. User just canceled waiting.
return true;
return POST_TIMER_RECORD_CANCEL_WAIT;
}
else
{
@ -282,14 +481,136 @@ bool TimerRecordDialog::RunWaitDialog()
// Let the caller handle cancellation or failure from recording progress.
if (updateResult == eProgressCancelled || updateResult == eProgressFailed)
return false;
return POST_TIMER_RECORD_CANCEL;
// Success, so let's automatically save it, for safety's sake.
// If user hadn't saved it before, they'll see the Save As dialog.
// If user had saved it before, it will safely be saved, automatically.
pProject->Save();
return ExecutePostRecordActions((updateResult == eProgressStopped));
}
return true;
int TimerRecordDialog::ExecutePostRecordActions(bool bWasStopped) {
// We no longer automatically (and silently) call ->Save() when the
// timer recording is completed!
// We can now Save and/or Export depending on the options selected by
// the user.
// Once completed, we can also close Audacity, restart the system or
// shutdown the system.
// If there was any error with the auto save or export then we will not do
// the actions requested and instead present an error mesasge to the user.
// Finally, if there is no post-record action selected then we will output
// a dialog detailing what has been carried out instead.
AudacityProject* pProject = GetActiveProject();
bool bSaveOK = false;
bool bExportOK = false;
int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
int iOverriddenAction = iPostRecordAction;
bool bErrorOverride = false;
// Do Automatic Save?
if (m_bAutoSaveEnabled) {
// MY: If this project has already been saved then simply execute a Save here
if (m_bProjectAlreadySaved) {
bSaveOK = pProject->Save();
}
else {
bSaveOK = pProject->SaveFromTimerRecording(m_fnAutoSaveFile);
}
}
// Do Automatic Export?
if (m_bAutoExportEnabled) {
bExportOK = pProject->ExportFromTimerRecording(m_fnAutoExportFile, m_iAutoExportFormat,
m_iAutoExportSubFormat, m_iAutoExportFilterIndex);
}
// Check if we need to override the post recording action
bErrorOverride = ((m_bAutoSaveEnabled && !bSaveOK) || (m_bAutoExportEnabled && !bExportOK));
if (bErrorOverride || bWasStopped) {
iPostRecordAction = POST_TIMER_RECORD_NOTHING;
}
if (iPostRecordAction == POST_TIMER_RECORD_NOTHING) {
// If there is no post-record action then we can show a message indicating what has been done
wxString sMessage = (bWasStopped ? _("Timer Recording Stopped.") :
_("Timer Recording Completed."));
if (m_bAutoSaveEnabled) {
if (bSaveOK) {
sMessage.Printf("%s\n\nRecording saved: %s",
sMessage, m_fnAutoSaveFile.GetFullPath());
}
else {
sMessage.Printf("%s\n\nError saving recording.", sMessage);
}
}
if (m_bAutoExportEnabled) {
if (bExportOK) {
sMessage.Printf("%s\n\nRecording exported: %s",
sMessage, m_fnAutoExportFile.GetFullPath());
}
else {
sMessage.Printf("%s\n\nError exporting recording.", sMessage);
}
}
if (bErrorOverride) {
if ((iOverriddenAction != iPostRecordAction) &&
(iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
// Inform the user that we have overridden the selected action
sMessage.Printf("%s\n\n'%s' has been canceled due to the error(s) noted above.",
sMessage,
m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
}
// Show Error Message Box
wxMessageBox(sMessage, _("Error"), wxICON_EXCLAMATION | wxOK);
}
else {
if (bWasStopped && (iOverriddenAction != POST_TIMER_RECORD_NOTHING)) {
sMessage.Printf("%s\n\n'%s' has been cancelled as the recording was stopped.",
sMessage,
m_pTimerAfterCompleteChoiceCtrl->GetString(iOverriddenAction));
}
wxMessageBox(sMessage, _("Timer Recording"), wxICON_INFORMATION | wxOK);
}
}
// MY: Lets do some actions that only apply to Exit/Restart/Shutdown
if (iPostRecordAction >= POST_TIMER_RECORD_CLOSE) {
do {
// Lets show a warning dialog telling the user what is about to happen.
// If the user no longer wants to carry out this action then they can click
// Cancel and we will do POST_TIMER_RECORD_NOTHING instead.
int iDelayOutcome = PreActionDelay(iPostRecordAction, (m_bAutoSaveEnabled && bSaveOK),
(m_bAutoExportEnabled && bExportOK));
if (iDelayOutcome != eProgressSuccess) {
// Cancel the action!
iPostRecordAction = POST_TIMER_RECORD_NOTHING;
// Set this to true to avoid any chance of the temp files being deleted
bErrorOverride = true;
break;
}
// If we have simply recorded, exported and then plan to Exit/Restart/Shutdown
// then we will have a temporary project setup. Let's get rid of that!
if (m_bAutoExportEnabled && !m_bAutoSaveEnabled) {
DirManager::CleanTempDir();
}
} while (false);
}
// Do we need to cleanup the orphaned temporary project?
if (m_bProjectCleanupRequired && !bErrorOverride) {
RemoveAllAutoSaveFiles();
}
// Return the action as required
return iPostRecordAction;
}
wxString TimerRecordDialog::GetDisplayDate( wxDateTime & dt )
@ -341,84 +662,166 @@ wxPrintf(wxT("%s\n"), dt.Format().c_str());
return dt.FormatDate() + wxT(" ") + dt.FormatTime();
}
TimerRecordPathCtrl * TimerRecordDialog::NewPathControl(wxWindow *wParent, const int iID,
const wxString &sCaption, const wxString &sValue)
{
TimerRecordPathCtrl * pTextCtrl;
pTextCtrl = safenew TimerRecordPathCtrl(wParent, iID, sValue);
pTextCtrl->SetName(sCaption);
return pTextCtrl;
}
void TimerRecordDialog::PopulateOrExchange(ShuttleGui& S)
{
bool bAutoSave = gPrefs->ReadBool("/TimerRecord/AutoSave", false);
bool bAutoExport = gPrefs->ReadBool("/TimerRecord/AutoExport", false);
int iPostTimerRecordAction = gPrefs->ReadLong("/TimerRecord/PostAction", 0);
S.SetBorder(5);
S.StartVerticalLay(true);
S.StartMultiColumn(2, wxCENTER);
{
/* i18n-hint: This string is used to configure the controls for times when the recording is
* started and stopped. As such it is important that only the alphabetic parts of the string
* are translated, with the numbers left exactly as they are.
* The 'h' indicates the first number displayed is hours, the 'm' indicates the second number
* displayed is minutes, and the 's' indicates that the third number displayed is seconds.
*/
wxString strFormat = _("099 h 060 m 060 s");
S.StartStatic(_("Start Date and Time"), true);
S.StartVerticalLay(true);
{
m_pDatePickerCtrl_Start =
safenew wxDatePickerCtrl(this, // wxWindow *parent,
ID_DATEPICKER_START, // wxWindowID id,
m_DateTime_Start); // const wxDateTime& dt = wxDefaultDateTime,
// const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
m_pDatePickerCtrl_Start->SetName(_("Start Date"));
m_pDatePickerCtrl_Start->SetRange(wxDateTime::Today(), wxInvalidDateTime); // No backdating.
S.AddWindow(m_pDatePickerCtrl_Start);
/* i18n-hint: This string is used to configure the controls for times when the recording is
* started and stopped. As such it is important that only the alphabetic parts of the string
* are translated, with the numbers left exactly as they are.
* The 'h' indicates the first number displayed is hours, the 'm' indicates the second number
* displayed is minutes, and the 's' indicates that the third number displayed is seconds.
*/
wxString strFormat = _("099 h 060 m 060 s");
S.StartStatic(_("Start Date and Time"), true);
{
m_pDatePickerCtrl_Start =
new wxDatePickerCtrl(this, // wxWindow *parent,
ID_DATEPICKER_START, // wxWindowID id,
m_DateTime_Start); // const wxDateTime& dt = wxDefaultDateTime,
// const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
m_pDatePickerCtrl_Start->SetName(_("Start Date"));
m_pDatePickerCtrl_Start->SetRange(wxDateTime::Today(), wxInvalidDateTime); // No backdating.
S.AddWindow(m_pDatePickerCtrl_Start);
m_pTimeTextCtrl_Start = safenew NumericTextCtrl(
NumericConverter::TIME, this, ID_TIMETEXT_START);
m_pTimeTextCtrl_Start->SetName(_("Start Time"));
m_pTimeTextCtrl_Start->SetFormatString(strFormat);
m_pTimeTextCtrl_Start->
SetValue(wxDateTime_to_AudacityTime(m_DateTime_Start));
S.AddWindow(m_pTimeTextCtrl_Start);
m_pTimeTextCtrl_Start->EnableMenu(false);
m_pTimeTextCtrl_Start = new NumericTextCtrl(
NumericConverter::TIME, this, ID_TIMETEXT_START);
m_pTimeTextCtrl_Start->SetName(_("Start Time"));
m_pTimeTextCtrl_Start->SetFormatString(strFormat);
m_pTimeTextCtrl_Start->
SetValue(wxDateTime_to_AudacityTime(m_DateTime_Start));
S.AddWindow(m_pTimeTextCtrl_Start);
m_pTimeTextCtrl_Start->EnableMenu(false);
}
S.EndStatic();
S.StartStatic(_("End Date and Time"), true);
{
m_pDatePickerCtrl_End =
new wxDatePickerCtrl(this, // wxWindow *parent,
ID_DATEPICKER_END, // wxWindowID id,
m_DateTime_End); // const wxDateTime& dt = wxDefaultDateTime,
// const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
m_pDatePickerCtrl_End->SetName(_("End Date"));
S.AddWindow(m_pDatePickerCtrl_End);
m_pTimeTextCtrl_End = new NumericTextCtrl(
NumericConverter::TIME, this, ID_TIMETEXT_END);
m_pTimeTextCtrl_End->SetName(_("End Time"));
m_pTimeTextCtrl_End->SetFormatString(strFormat);
m_pTimeTextCtrl_End->SetValue(wxDateTime_to_AudacityTime(m_DateTime_End));
S.AddWindow(m_pTimeTextCtrl_End);
m_pTimeTextCtrl_End->EnableMenu(false);
}
S.EndStatic();
S.StartStatic(_("Duration"), true);
{
/* i18n-hint: This string is used to configure the controls which shows the recording
* duration. As such it is important that only the alphabetic parts of the string
* are translated, with the numbers left exactly as they are.
* The string 'days' indicates that the first number in the control will be the number of days,
* then the 'h' indicates the second number displayed is hours, the 'm' indicates the third
* number displayed is minutes, and the 's' indicates that the fourth number displayed is
* seconds.
*/
wxString strFormat1 = _("099 days 024 h 060 m 060 s");
m_pTimeTextCtrl_Duration = new NumericTextCtrl(NumericConverter::TIME, this, ID_TIMETEXT_DURATION);
m_pTimeTextCtrl_Duration->SetName(_("Duration"));
m_pTimeTextCtrl_Duration->SetFormatString(strFormat1);
m_pTimeTextCtrl_Duration->SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble());
S.AddWindow(m_pTimeTextCtrl_Duration);
m_pTimeTextCtrl_Duration->EnableMenu(false);
}
S.EndStatic();
}
S.EndStatic();
S.EndVerticalLay();
S.StartStatic(_("End Date and Time"), true);
S.StartVerticalLay(true);
{
m_pDatePickerCtrl_End =
safenew wxDatePickerCtrl(this, // wxWindow *parent,
ID_DATEPICKER_END, // wxWindowID id,
m_DateTime_End); // const wxDateTime& dt = wxDefaultDateTime,
// const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDP_DEFAULT | wxDP_SHOWCENTURY, const wxValidator& validator = wxDefaultValidator, const wxString& name = "datectrl")
m_pDatePickerCtrl_End->SetRange(m_DateTime_Start, wxInvalidDateTime); // No backdating.
m_pDatePickerCtrl_End->SetName(_("End Date"));
S.AddWindow(m_pDatePickerCtrl_End);
S.StartStatic(_("Automatic Save"), true);
{
// If checked, the project will be saved when the recording is completed
m_pTimerAutoSaveCheckBoxCtrl = S.Id(ID_AUTOSAVE_CHECKBOX).AddCheckBox(_("Enable &Automatic Save?"),
(bAutoSave ? "true" : "false"));
S.StartMultiColumn(3, wxEXPAND);
{
wxString sInitialValue = wxT("");
AudacityProject* pProject = GetActiveProject();
wxString sSaveValue = pProject->GetFileName();
if (sSaveValue != wxEmptyString) {
m_fnAutoSaveFile.Assign(sSaveValue);
sInitialValue = _("Current Project");
}
S.AddPrompt(_("Save Project As:"));
m_pTimerSavePathTextCtrl = NewPathControl(this, ID_AUTOSAVEPATH_TEXT, _("Save Project As:"), sInitialValue);
m_pTimerSavePathTextCtrl->SetEditable(false);
S.AddWindow(m_pTimerSavePathTextCtrl);
m_pTimerSavePathButtonCtrl = S.Id(ID_AUTOSAVEPATH_BUTTON).AddButton(_("Select..."));
}
S.EndMultiColumn();
}
S.EndStatic();
m_pTimeTextCtrl_End = safenew NumericTextCtrl(
NumericConverter::TIME, this, ID_TIMETEXT_END);
m_pTimeTextCtrl_End->SetName(_("End Time"));
m_pTimeTextCtrl_End->SetFormatString(strFormat);
m_pTimeTextCtrl_End->SetValue(wxDateTime_to_AudacityTime(m_DateTime_End));
S.AddWindow(m_pTimeTextCtrl_End);
m_pTimeTextCtrl_End->EnableMenu(false);
}
S.EndStatic();
S.StartStatic(_("Automatic Export"), true);
{
m_pTimerAutoExportCheckBoxCtrl = S.Id(ID_AUTOEXPORT_CHECKBOX).AddCheckBox(_("Enable Automatic &Export?"), (bAutoExport ? "true" : "false"));
S.StartMultiColumn(3, wxEXPAND);
{
S.AddPrompt(_("Export Project As:"));
m_pTimerExportPathTextCtrl = NewPathControl(this, ID_AUTOEXPORTPATH_TEXT, _("Export Project As:"), _(""));
m_pTimerExportPathTextCtrl->SetEditable(false);
S.AddWindow(m_pTimerExportPathTextCtrl);
m_pTimerExportPathButtonCtrl = S.Id(ID_AUTOEXPORTPATH_BUTTON).AddButton(_("Select..."));
}
S.EndMultiColumn();
}
S.EndStatic();
S.StartStatic(_("Options"), true);
{
wxArrayString arrayOptions;
arrayOptions.Add(_("Do Nothing"));
arrayOptions.Add(_("Exit Audacity"));
arrayOptions.Add(_("Restart System"));
arrayOptions.Add(_("Shutdown System"));
m_sTimerAfterCompleteOptionsArray.Add(arrayOptions.Item(0));
m_sTimerAfterCompleteOptionsArray.Add(arrayOptions.Item(1));
#ifdef __WINDOWS__
m_sTimerAfterCompleteOptionsArray.Add(arrayOptions.Item(2));
m_sTimerAfterCompleteOptionsArray.Add(arrayOptions.Item(3));
#endif
m_sTimerAfterCompleteOption = arrayOptions.Item(iPostTimerRecordAction);
m_pTimerAfterCompleteChoiceCtrl = S.AddChoice(_("After Recording Completes:"),
m_sTimerAfterCompleteOption,
&m_sTimerAfterCompleteOptionsArray);
}
S.EndStatic();
S.StartStatic(_("Duration"), true);
{
/* i18n-hint: This string is used to configure the controls which shows the recording
* duration. As such it is important that only the alphabetic parts of the string
* are translated, with the numbers left exactly as they are.
* The string 'days' indicates that the first number in the control will be the number of days,
* then the 'h' indicates the second number displayed is hours, the 'm' indicates the third
* number displayed is minutes, and the 's' indicates that the fourth number displayed is
* seconds.
*/
wxString strFormat1 = _("099 days 024 h 060 m 060 s");
m_pTimeTextCtrl_Duration = safenew NumericTextCtrl(
NumericConverter::TIME, this, ID_TIMETEXT_DURATION);
m_pTimeTextCtrl_Duration->SetName(_("Duration"));
m_pTimeTextCtrl_Duration->SetFormatString(strFormat1);
m_pTimeTextCtrl_Duration->
SetValue(m_TimeSpan_Duration.GetSeconds().ToDouble());
S.AddWindow(m_pTimeTextCtrl_Duration);
m_pTimeTextCtrl_Duration->EnableMenu(false);
}
S.EndStatic();
S.EndVerticalLay();
}
S.EndVerticalLay();
S.EndMultiColumn();
S.AddStandardButtons();
@ -426,6 +829,9 @@ void TimerRecordDialog::PopulateOrExchange(ShuttleGui& S)
Fit();
SetMinSize(GetSize());
Center();
EnableDisableAutoControls(bAutoSave, CONTROL_GROUP_SAVE);
EnableDisableAutoControls(bAutoExport, CONTROL_GROUP_EXPORT);
}
bool TimerRecordDialog::TransferDataFromWindow()
@ -455,6 +861,18 @@ bool TimerRecordDialog::TransferDataFromWindow()
m_TimeSpan_Duration = m_DateTime_End - m_DateTime_Start;
// Pull the settings from the auto save/export controls and write to the pref file
m_bAutoSaveEnabled = m_pTimerAutoSaveCheckBoxCtrl->GetValue();
m_bAutoExportEnabled = m_pTimerAutoExportCheckBoxCtrl->GetValue();
// MY: Obtain the index from the choice control so we can save to the prefs file
int iPostRecordAction = m_pTimerAfterCompleteChoiceCtrl->GetSelection();
// Save the options back to the prefs file
gPrefs->Write("/TimerRecord/AutoSave", m_bAutoSaveEnabled);
gPrefs->Write("/TimerRecord/AutoExport", m_bAutoExportEnabled);
gPrefs->Write("/TimerRecord/PostAction", iPostRecordAction);
return true;
}
@ -501,3 +919,40 @@ int TimerRecordDialog::WaitForStart()
}
return updateResult;
}
int TimerRecordDialog::PreActionDelay(int iActionIndex, bool bSaved, bool bExported)
{
wxString sMessage;
wxString sAction = m_pTimerAfterCompleteChoiceCtrl->GetString(iActionIndex);
wxString sDone = "";
if (bSaved && bExported) {
sDone = "Saved and Exported";
}
else if (bSaved) {
sDone = "Saved";
}
else if (bExported) {
sDone = "Exported";
}
sMessage.Printf(_("Timer Recording completed: Recording has been %s as instructed.\n\n'%s' will occur shortly...\n"),
sDone, sAction);
wxDateTime dtNow = wxDateTime::UNow();
wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0);
wxDateTime dtActionTime = dtNow.Add(tsWait);
TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(),
_("Audacity Timer Record - Waiting"),
sMessage,
pdlgHideStopButton);
int iUpdateResult = eProgressSuccess;
bool bIsTime = false;
while (iUpdateResult == eProgressSuccess && !bIsTime)
{
wxMilliSleep(10);
iUpdateResult = dlgAction.Update();
bIsTime = (dtActionTime <= wxDateTime::UNow());
}
return iUpdateResult;
}

View File

@ -17,24 +17,46 @@
#define __AUDACITY_TIMERRECORD_DIALOG__
#include <wx/dialog.h>
#include <wx/textctrl.h>
#include <wx/datectrl.h>
#include <wx/calctrl.h>
#include <wx/timer.h>
#include <wx/checkbox.h>
#include "export/Export.h"
class wxTimerEvent;
class NumericTextCtrl;
class ShuttleGui;
class TimerRecordPathCtrl;
class TimerRecordPathCtrl final : public wxTextCtrl
{
// MY: Class that inherits from the wxTextCtrl class.
// We override AcceptsFocusFromKeyboard in order to add
// the text controls to the Tab Order.
public:
TimerRecordPathCtrl(wxWindow * parent, wxWindowID id, const wxString &value
= wxEmptyString, const wxPoint &pos = wxDefaultPosition, const wxSize &
size = wxDefaultSize, long style = 0, const wxValidator & validator =
wxDefaultValidator, const wxString & name = wxTextCtrlNameStr)
:wxTextCtrl(parent, id, value, pos, size, style, validator, name) {};
~TimerRecordPathCtrl() {};
virtual bool AcceptsFocusFromKeyboard() const override {
return true;
}
};
class TimerRecordDialog final : public wxDialog
{
public:
TimerRecordDialog(wxWindow* parent);
TimerRecordDialog(wxWindow* parent, bool bAlreadySaved);
~TimerRecordDialog();
void OnTimer(wxTimerEvent& event);
///Runs the wait for start dialog. Returns false if the user clicks stop.
bool RunWaitDialog();
int RunWaitDialog();
private:
void OnDatePicker_Start(wxDateEvent& event);
@ -54,6 +76,25 @@ private:
void UpdateEnd(); // Update m_DateTime_End and ctrls based on m_DateTime_Start and m_TimeSpan_Duration.
int WaitForStart();
// Timer Recording Automation Control Events
void OnAutoSavePathButton_Click(wxCommandEvent& event);
void OnAutoExportPathButton_Click(wxCommandEvent& event);
void OnAutoSaveCheckBox_Change(wxCommandEvent& event);
void OnAutoExportCheckBox_Change(wxCommandEvent& event);
// Timer Recording Automation Routines
void EnableDisableAutoControls(bool bEnable, int iControlGoup);
void UpdateTextBoxControls();
// Tidy up after Timer Recording
bool HaveFilesToRecover();
bool RemoveAllAutoSaveFiles();
// Add Path Controls to Form
TimerRecordPathCtrl *NewPathControl(wxWindow *wParent, const int iID, const wxString &sCaption, const wxString &sValue);
int ExecutePostRecordActions(bool bWasStopped);
int PreActionDelay(int iActionIndex, bool bSaved, bool bExported);
private:
wxDateTime m_DateTime_Start;
wxDateTime m_DateTime_End;
@ -70,6 +111,34 @@ private:
wxTimer m_timer;
// Controls for Auto Save/Export
wxCheckBox *m_pTimerAutoSaveCheckBoxCtrl;
TimerRecordPathCtrl *m_pTimerSavePathTextCtrl;
wxButton *m_pTimerSavePathButtonCtrl;
wxCheckBox *m_pTimerAutoExportCheckBoxCtrl;
TimerRecordPathCtrl *m_pTimerExportPathTextCtrl;
wxButton *m_pTimerExportPathButtonCtrl;
// After Timer Record Options Choice
wxChoice *m_pTimerAfterCompleteChoiceCtrl;
// After Timer Record do we need to clean up?
bool m_bProjectCleanupRequired;
// Variables for the Auto Save/Export
bool m_bAutoSaveEnabled;
wxFileName m_fnAutoSaveFile;
bool m_bAutoExportEnabled;
wxFileName m_fnAutoExportFile;
int m_iAutoExportFormat;
int m_iAutoExportSubFormat;
int m_iAutoExportFilterIndex;
bool m_bProjectAlreadySaved;
// Variables for After Timer Recording Option
wxString m_sTimerAfterCompleteOption;
wxArrayString m_sTimerAfterCompleteOptionsArray;
DECLARE_EVENT_TABLE();
};

View File

@ -640,9 +640,9 @@ bool Exporter::GetFilename()
!mFilename.FileExists()) {
// Warn and return to the dialog
wxMessageBox(_("You are attempting to overwrite an aliased file that is missing.\n\
The file cannot be written because the path is needed to restore the original audio to the project.\n\
Choose File > Check Dependencies to view the locations of all missing files.\n\
If you still wish to export, please choose a different filename or folder."));
The file cannot be written because the path is needed to restore the original audio to the project.\n\
Choose File > Check Dependencies to view the locations of all missing files.\n\
If you still wish to export, please choose a different filename or folder."));
overwritingMissingAlias = true;
}
}
@ -886,6 +886,253 @@ void Exporter::OnFilterChanged(wxFileCtrlEvent & evt)
mBook->ChangeSelection(index);
}
bool Exporter::ProcessFromTimerRecording(AudacityProject *project,
bool selectedOnly,
double t0,
double t1,
wxFileName fnFile,
int iFormat,
int iSubFormat,
int iFilterIndex)
{
// Save parms
mProject = project;
mSelectedOnly = selectedOnly;
mT0 = t0;
mT1 = t1;
// Auto Export Parameters
mFilename = fnFile;
mFormat = iFormat;
mSubFormat = iSubFormat;
mFilterIndex = iFilterIndex;
// Gather track information
if (!ExamineTracks()) {
return false;
}
// Check for down mixing
if (!CheckMix()) {
return false;
}
// Ensure filename doesn't interfere with project files.
if (!CheckFilename()) {
return false;
}
// Export the tracks
bool success = ExportTracks();
// Get rid of mixerspec
if (mMixerSpec) {
delete mMixerSpec;
mMixerSpec = NULL;
}
return success;
}
int Exporter::GetAutoExportFormat() {
return mFormat;
}
int Exporter::GetAutoExportSubFormat() {
return mSubFormat;
}
int Exporter::GetAutoExportFilterIndex() {
return mFormat;
}
wxFileName Exporter::GetAutoExportFileName() {
return mFilename;
}
bool Exporter::SetAutoExportOptions(AudacityProject *project) {
mFormat = -1;
mProject = project;
wxString maskString;
wxString defaultFormat = gPrefs->Read(wxT("/Export/Format"),
wxT("WAV"));
mFilterIndex = 0;
for (size_t i = 0; i < mPlugins.GetCount(); i++) {
for (int j = 0; j < mPlugins[i]->GetFormatCount(); j++)
{
maskString += mPlugins[i]->GetMask(j) + wxT("|");
if (mPlugins[i]->GetFormat(j) == defaultFormat) {
mFormat = i;
mSubFormat = j;
}
if (mFormat == -1) mFilterIndex++;
}
}
if (mFormat == -1)
{
mFormat = 0;
mFilterIndex = 0;
}
maskString.RemoveLast();
mFilename.SetPath(gPrefs->Read(wxT("/Export/Path"), ::wxGetCwd()));
mFilename.SetName(mProject->GetName());
while (true) {
// Must reset each iteration
mBook = NULL;
FileDialog fd(mProject,
mFileDialogTitle,
mFilename.GetPath(),
mFilename.GetFullName(),
maskString,
wxFD_SAVE | wxRESIZE_BORDER);
mDialog = &fd;
mDialog->PushEventHandler(this);
fd.SetUserPaneCreator(CreateUserPaneCallback, (wxUIntPtr) this);
fd.SetFilterIndex(mFilterIndex);
int result = fd.ShowModal();
mDialog->PopEventHandler();
if (result == wxID_CANCEL) {
return false;
}
mFilename = fd.GetPath();
if (mFilename == wxT("")) {
return false;
}
mFormat = fd.GetFilterIndex();
mFilterIndex = fd.GetFilterIndex();
int c = 0;
for (size_t i = 0; i < mPlugins.GetCount(); i++)
{
for (int j = 0; j < mPlugins[i]->GetFormatCount(); j++)
{
if (mFilterIndex == c)
{
mFormat = i;
mSubFormat = j;
}
c++;
}
}
wxString ext = mFilename.GetExt();
wxString defext = mPlugins[mFormat]->GetExtension(mSubFormat).Lower();
//
// Check the extension - add the default if it's not there,
// and warn user if it's abnormal.
//
if (ext.IsEmpty()) {
//
// Make sure the user doesn't accidentally save the file
// as an extension with no name, like just plain ".wav".
//
if (mFilename.GetName().Left(1) == wxT(".")) {
wxString prompt = _("Are you sure you want to export the file as \"") +
mFilename.GetFullName() +
wxT("\"?\n");
int action = wxMessageBox(prompt,
_("Warning"),
wxYES_NO | wxICON_EXCLAMATION);
if (action != wxYES) {
continue;
}
}
mFilename.SetExt(defext);
}
else if (!mPlugins[mFormat]->CheckFileName(mFilename, mSubFormat))
{
continue;
}
else if (!ext.IsEmpty() && !mPlugins[mFormat]->IsExtension(ext, mSubFormat) && ext.CmpNoCase(defext)) {
wxString prompt;
prompt.Printf(_("You are about to export a %s file with the name \"%s\".\n\n\
Normally these files end in \".%s\", and some programs\n\
will not open files with nonstandard extensions.\n\n\
Are you sure you want to export the file under this name?"),
mPlugins[mFormat]->GetFormat(mSubFormat).c_str(),
mFilename.GetFullName().c_str(),
defext.c_str());
int action = wxMessageBox(prompt,
_("Warning"),
wxYES_NO | wxICON_EXCLAMATION);
if (action != wxYES) {
continue;
}
}
if (mFilename.GetFullPath().Length() >= 256) {
wxMessageBox(_("Sorry, pathnames longer than 256 characters not supported."));
continue;
}
// Check to see if we are writing to a path that a missing aliased file existed at.
// This causes problems for the exporter, so we don't allow it.
// Overwritting non-missing aliased files is okay.
// Also, this can only happen for uncompressed audio.
bool overwritingMissingAlias;
overwritingMissingAlias = false;
for (size_t i = 0; i < gAudacityProjects.GetCount(); i++) {
AliasedFileArray aliasedFiles;
FindDependencies(gAudacityProjects[i], &aliasedFiles);
size_t j;
for (j = 0; j< aliasedFiles.GetCount(); j++) {
if (mFilename.GetFullPath() == aliasedFiles[j].mFileName.GetFullPath() &&
!mFilename.FileExists()) {
// Warn and return to the dialog
wxMessageBox(_("You are attempting to overwrite an aliased file that is missing.\n\
The file cannot be written because the path is needed to restore the original audio to the project.\n\
Choose File > Check Dependencies to view the locations of all missing files.\n\
If you still wish to export, please choose a different filename or folder."));
overwritingMissingAlias = true;
}
}
}
if (overwritingMissingAlias)
continue;
if (mFilename.FileExists()) {
wxString prompt;
prompt.Printf(_("A file named \"%s\" already exists. Replace?"),
mFilename.GetFullPath().c_str());
int action = wxMessageBox(prompt,
_("Warning"),
wxYES_NO | wxICON_EXCLAMATION);
if (action != wxYES) {
continue;
}
}
break;
}
// Let user edit MetaData
if (mPlugins[mFormat]->GetCanMetaData(mSubFormat)) {
if (!(project->DoEditMetadata(_("Edit Metadata Tags for Export"),
_("Exported Tags"), mProject->GetShowId3Dialog()))) {
return false;
}
}
return true;
}
//----------------------------------------------------------------------------
// ExportMixerPanel
//----------------------------------------------------------------------------

View File

@ -153,6 +153,21 @@ public:
const ExportPluginArray GetPlugins();
// Auto Export from Timer Recording
bool ProcessFromTimerRecording(AudacityProject *project,
bool selectedOnly,
double t0,
double t1,
wxFileName fnFile,
int iFormat,
int iSubFormat,
int iFilterIndex);
bool SetAutoExportOptions(AudacityProject *project);
int GetAutoExportFormat();
int GetAutoExportSubFormat();
int GetAutoExportFilterIndex();
wxFileName GetAutoExportFileName();
private:
bool ExamineTracks();