1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-08-07 07:39:29 +02:00

Capture screenshots of effects.

Only available if you define EXPERIMENTAL_DOCS_AUTOMATION.
This code is still a bit ropey and not suited for prime time, but fine for our own (careful) use in preparing the manual.

It captures most of the effects, generator and analyze built ins and nyquist dialogs.  Use it by creating a track, making a selection and then clicking the 'All Effects' button in the screenshot tools.  The dialogs will be captured to your user directory.
This commit is contained in:
James Crook 2017-06-02 23:52:53 +01:00
parent 7e691ddf38
commit acd55e95db
7 changed files with 217 additions and 12 deletions

View File

@ -48,12 +48,19 @@
// feature to link audio tracks to a label track
#define EXPERIMENTAL_SYNC_LOCK
// JKC: Enable to get extra buttons in the screenshot tools.
//#define EXPERIMENTAL_DOCS_AUTOMATION
// JKC: Enable to get experiemental code to move
// mod-script-pipe towards being mainstream.
//#define EXPERIMENTAL_AUTOMATION
// DA: Enables dark audacity theme and customisations.
//#define EXPERIMENTAL_DA
// experimental theming
// Work in progress, June-2008.
// This mostly sets up a weird color scheme currently.
// EXPERIMENTAL_THEMING is mostly mainstream now.
// the define is still present to mark out old code before theming, that we might
// conceivably need.
// TODO: Agree on and then tidy this code.
#define EXPERIMENTAL_THEMING
//August 2009 - Theming not locked down enough for a stable release.

View File

@ -71,6 +71,7 @@ class ScreenFrame final : public wxFrame
void OnCaptureFullScreen(wxCommandEvent & event);
void OnCaptureToolbars(wxCommandEvent & event);
void OnCaptureMenus(wxCommandEvent & event);
void OnCaptureEffects(wxCommandEvent & event);
void OnCaptureSelectionBar(wxCommandEvent & event);
void OnCaptureTools(wxCommandEvent & event);
void OnCaptureTransport(wxCommandEvent & event);
@ -180,6 +181,7 @@ enum
IdCaptureToolbars,
IdCaptureMenus,
IdCaptureEffects,
IdCaptureSelectionBar,
IdCaptureTools,
IdCaptureTransport,
@ -225,6 +227,7 @@ BEGIN_EVENT_TABLE(ScreenFrame, wxFrame)
EVT_BUTTON(IdCaptureToolbars, ScreenFrame::OnCaptureToolbars)
EVT_BUTTON(IdCaptureMenus, ScreenFrame::OnCaptureMenus)
EVT_BUTTON(IdCaptureEffects, ScreenFrame::OnCaptureEffects)
EVT_BUTTON(IdCaptureSelectionBar, ScreenFrame::OnCaptureSelectionBar)
EVT_BUTTON(IdCaptureTools, ScreenFrame::OnCaptureTools)
EVT_BUTTON(IdCaptureTransport, ScreenFrame::OnCaptureTransport)
@ -386,17 +389,25 @@ void ScreenFrame::PopulateOrExchange(ShuttleGui & S)
S.StartHorizontalLay();
{
S.Id(IdCaptureToolbars).AddButton(_("All Toolbars"));
#ifdef EXPERIMENTAL_DOCS_AUTOMATION
S.Id(IdCaptureMenus).AddButton(_("All Menus"));
S.Id(IdCaptureSelectionBar).AddButton(_("SelectionBar"));
S.Id(IdCaptureTools).AddButton(_("Tools"));
S.Id(IdCaptureTransport).AddButton(_("Transport"));
S.Id(IdCaptureEffects).AddButton(_("All Effects"));
#endif
}
S.EndHorizontalLay();
S.StartHorizontalLay();
{
S.Id(IdCaptureSelectionBar).AddButton(_("SelectionBar"));
S.Id(IdCaptureTools).AddButton(_("Tools"));
S.Id(IdCaptureTransport).AddButton(_("Transport"));
S.Id(IdCaptureMixer).AddButton(_("Mixer"));
S.Id(IdCaptureMeter).AddButton(_("Meter"));
}
S.EndHorizontalLay();
S.StartHorizontalLay();
{
S.Id(IdCaptureEdit).AddButton(_("Edit"));
S.Id(IdCaptureDevice).AddButton(_("Device"));
S.Id(IdCaptureTranscription).AddButton(_("Transcription"));
@ -613,6 +624,11 @@ void ScreenFrame::OnCaptureMenus(wxCommandEvent & WXUNUSED(event))
DoCapture(wxT("menus"));
}
void ScreenFrame::OnCaptureEffects(wxCommandEvent & WXUNUSED(event))
{
DoCapture(wxT("effects"));
}
void ScreenFrame::OnCaptureSelectionBar(wxCommandEvent & WXUNUSED(event))
{
DoCapture(wxT("selectionbar"));

View File

@ -1316,12 +1316,15 @@ bool CommandManager::HandleMenuID(int id, CommandFlag flags, CommandMask mask)
/// code to run.
bool CommandManager::HandleTextualCommand(wxString & Str, CommandFlag flags, CommandMask mask)
{
if( Str.IsEmpty() )
return false;
// Linear search for now...
for (const auto &entry : mCommandList)
{
if (!entry->multi)
{
if( Str.IsSameAs( entry->name ))
// Testing against labelPrefix too allows us to call Nyquist functions by name.
if( Str.IsSameAs( entry->name ) || Str.IsSameAs( entry->labelPrefix ))
{
return HandleCommandEntry( entry.get(), flags, mask);
}

View File

@ -15,6 +15,11 @@ project window.
*//*******************************************************************/
/* TODO: JKC: The screenshot code is very verbose and should be made
much shorter. It could work from a single list of function
names and functors.
*/
#include "ScreenshotCommand.h"
#include "CommandTargets.h"
#include "../Project.h"
@ -94,6 +99,23 @@ static wxBitmap DoGetAsBitmap(const wxRect *subrect)
}
#endif
// This static variable is used to get from an idle event to the screenshot
// command that caused the idle event interception to be set up.
ScreenshotCommand * ScreenshotCommand::mpShooter=NULL;
// IdleHandler is expected to be called from EVT_IDLE when a dialog has been
// fully created. Usually the dialog will have been created by invoking
// an effects gui.
void IdleHandler(wxIdleEvent& event){
wxWindow * pWin = dynamic_cast<wxWindow*>(event.GetEventObject());
wxASSERT( pWin );
pWin->Unbind(wxEVT_IDLE, IdleHandler);
// We have the relevant window, so go and capture it.
if( ScreenshotCommand::mpShooter )
ScreenshotCommand::mpShooter->CaptureWindowOnIdle( pWin );
}
wxTopLevelWindow *ScreenshotCommand::GetFrontWindow(AudacityProject *project)
{
wxWindow *front = NULL;
@ -331,6 +353,138 @@ void ScreenshotCommand::CaptureMenus(wxMenuBar*pBar, const wxString &fileName)
#endif
}
void ScreenshotCommand::CaptureWindowOnIdle( wxWindow * pWin )
{
wxDialog * pDlg = dynamic_cast<wxDialog*>(pWin);
if( !pDlg ){
wxLogDebug("Event from bogus dlg" );
return;
}
wxPoint Pos = pDlg->GetScreenPosition();
wxSize Siz = pDlg->GetSize();
wxString Name = mDirToWriteTo;
wxString Title = pDlg->GetTitle();
// Remove '/' from "Sliding Time Scale/Pitch Shift..."
// and any other effects that have illegal filename chanracters.
Title.Replace( "/", "" );
Name.Replace( "effects000", Title );
int x = 0, y = 0;
int width, height;
wxLogDebug("Taking screenshot of window %s (%i,%i,%i,%i)", Name,
Pos.x, Pos.y, Siz.x, Siz.y );
// This delay is needed, as dialogs take a moment or two to fade in.
wxMilliSleep( 200 );
// JKC: The border of 7 pixels was determined from a trial capture and then measuring
// in the GIMP. I'm unsure where the border comes from.
Capture( Name, pDlg, (int)Pos.x+7, (int)Pos.y, (int)Siz.x-14, (int)Siz.y-7 );
// We've captured the dialog, so now dismiss the dialog.
wxCommandEvent Evt( wxEVT_BUTTON, wxID_CANCEL );
pDlg->GetEventHandler()->AddPendingEvent( Evt );
}
void ScreenshotCommand::CaptureEffects( AudacityProject * pProject, const wxString &fileName ){
fileName;//compiler food.
CommandManager * pMan = pProject->GetCommandManager();
wxString Str;
// Yucky static variables. Is there a better way? The problem is that we need the
// idle callback to know more about what to do.
mDirToWriteTo = fileName;
mpShooter = this;
#define CAPTURE_NYQUIST_TOO
// Commented out the effects that don't have dialogs.
// Also any problematic ones,
const wxString EffectNames[] = {
"Amplify...",
//"Auto Duck...", // needs a track below.
"Bass and Treble...",
"Change Pitch...",
"Change Speed...",
"Change Tempo...",
"Click Removal...",
"Compressor...",
"Distortion...",
"Echo...",
"Equalization...",
//"Fade In",
//"Fade Out",
//"Invert",
//"Noise Reduction...", // Exits twice...
"Normalize...",
"Paulstretch...",
"Phaser...",
//"Repair",
"Repeat...",
"Reverb...",
//"Reverse",
"Sliding Time Scale/Pitch Shift...",
"Truncate Silence...",
"Wahwah...",
// Sole LADSPA effect...
//"SC4...", //Has 'Close' rather than 'Cancel'.
#ifdef CAPTURE_NYQUIST_TOO
"Adjustable Fade...",
"Clip Fix...",
//"Crossfade Clips",
"Crossfade Tracks...",
"Delay...",
"High Pass Filter...",
"Limiter...",
"Low Pass Filter...",
"Notch Filter...",
"Nyquist Prompt...",
//"Spectral edit multi tool",
//"Spectral edit parametric EQ...", // Needs a spectral selection.
//"Spectral edit shelves...",
//"Studio Fade Out",
"Tremolo...",
"Vocal Reduction and Isolation...",
"Vocal Remover...",
"Vocoder..."
#endif
// Generators.....
"Chirp...",
"DTMF Tones...",
"Noise...",
"Silence...",
"Tone...",
#ifdef CAPTURE_NYQUIST_TOO
"Pluck...",
"Rhythm Track...",
"Risset Drum...",
"Sample Data Import...",
#endif
// Analyzers...
"Contrast...",
"Plot Spectrum...",
"Find Clipping...",
#ifdef CAPTURE_NYQUIST_TOO
"Beat Finder...",
"Regular Interval Labels...",
"Sample Data Export...",
"Silence Finder...",
"Sound Finder...",
#endif
};
for( int i=0;i<sizeof(EffectNames)/sizeof(EffectNames[0]);i++){
// The handler is cleared each time it is used.
Effect::SetIdleHandler( IdleHandler );
Str = EffectNames[i];
pMan->HandleTextualCommand( Str, AlwaysEnabledFlag, AlwaysEnabledFlag );
// This sleep is not needed, but gives user a chance to see the
// dialogs as they whizz by.
wxMilliSleep( 200 );
}
}
wxString ScreenshotCommandType::BuildName()
{
return wxT("Screenshot");
@ -345,6 +499,7 @@ void ScreenshotCommandType::BuildSignature(CommandSignature &signature)
captureModeValidator->AddOption(wxT("fullscreen"));
captureModeValidator->AddOption(wxT("toolbars"));
captureModeValidator->AddOption(wxT("menus"));
captureModeValidator->AddOption(wxT("effects"));
captureModeValidator->AddOption(wxT("selectionbar"));
captureModeValidator->AddOption(wxT("tools"));
captureModeValidator->AddOption(wxT("transport"));
@ -493,6 +648,10 @@ bool ScreenshotCommand::Apply(CommandExecutionContext context)
{
CaptureMenus(context.GetProject()->GetMenuBar(), fileName);
}
else if (captureMode.IsSameAs(wxT("effects")))
{
CaptureEffects(context.GetProject(), fileName);
}
else if (captureMode.IsSameAs(wxT("selectionbar")))
{
CaptureDock(context.GetProject()->GetToolManager()->GetBotDock(), fileName);

View File

@ -39,19 +39,25 @@ private:
bool mBackground;
wxColour mBackColor;
wxString mDirToWriteTo;
wxString MakeFileName(const wxString &path, const wxString &basename);
wxRect GetBackgroundRect();
void CaptureToolbar(ToolManager *man, int type, const wxString &name);
void CaptureDock(wxWindow *win, const wxString &fileName);
void CaptureMenus(wxMenuBar*pBar, const wxString &fileName);
void CaptureEffects( AudacityProject * pProject, const wxString &fileName );
void Capture(const wxString &basename,
wxWindow *window,
int x, int y, int width, int height,
bool bg = false);
void CaptureToolbar(ToolManager *man, int type, const wxString &name);
void CaptureDock(wxWindow *win, const wxString &fileName);
void CaptureMenus(wxMenuBar*pBar, const wxString &fileName);
public:
static ScreenshotCommand * mpShooter;
void CaptureWindowOnIdle( wxWindow * pWin );
wxTopLevelWindow *GetFrontWindow(AudacityProject *project);
ScreenshotCommand(CommandType &type,
std::unique_ptr<CommandOutputTarget> &&output,

View File

@ -88,6 +88,9 @@ const wxString Effect::kFactoryPresetIdent = wxT("Factory Preset:");
const wxString Effect::kCurrentSettingsIdent = wxT("<Current Settings>");
const wxString Effect::kFactoryDefaultsIdent = wxT("<Factory Defaults>");
// static member variable.
void (*Effect::mIdleHandler)(wxIdleEvent& event) = NULL;
WX_DECLARE_VOIDPTR_HASH_MAP( bool, t2bHash );
Effect::Effect()
@ -541,11 +544,19 @@ bool Effect::ShowInterface(wxWindow *parent, bool forceModal)
return false;
}
mUIDialog->Layout();
mUIDialog->Fit();
mUIDialog->SetMinSize(mUIDialog->GetSize());
if (SupportsRealtime() && !forceModal)
// Idle event handler is used for example to take a screenshot.
if( mIdleHandler != NULL ){
mUIDialog->Bind( wxEVT_IDLE, mIdleHandler );
mIdleHandler = NULL;
forceModal = true;
}
if( SupportsRealtime() && !forceModal )
{
mUIDialog->Show();
cleanup.release();
@ -2828,7 +2839,6 @@ EffectUIHost::EffectUIHost(wxWindow *parent,
mEnabled = true;
mPlayPos = 0.0;
mClient->SetHostUI(this);
}

View File

@ -22,6 +22,7 @@
#include <wx/intl.h>
#include <wx/string.h>
#include <wx/tglbtn.h>
#include <wx/event.h> // for idle event.
class wxCheckBox;
class wxChoice;
@ -259,6 +260,7 @@ class AUDACITY_DLL_API Effect /* not final */ : public wxEvtHandler,
/* not virtual */ bool IsRealtimeActive();
virtual bool IsHidden();
static void SetIdleHandler( void (*pHandler)(wxIdleEvent& event) ){mIdleHandler=pHandler;};
//
// protected virtual methods
@ -437,6 +439,8 @@ protected:
// may be needed by any particular subclass of Effect.
//
protected:
static void (*mIdleHandler)(wxIdleEvent& event);
ProgressDialog *mProgress; // Temporary pointer, NOT deleted in destructor.
double mProjectRate; // Sample rate of the project - NEW tracks should
// be created with this rate...