1 /*
2  * Copyright (C) 2002 - David W. Durham
3  *
4  * This file is part of ReZound, an audio editing application.
5  *
6  * ReZound is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published
8  * by the Free Software Foundation; either version 2 of the License,
9  * or (at your option) any later version.
10  *
11  * ReZound is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
19  */
20 
21 #ifndef __AAction_H__
22 #define __AAction_H__
23 
24 #include "../../config/common.h"
25 
26 class AActionFactory;
27 class AAction;
28 
29 
30 
31 #include <string>
32 #include <vector>
33 #include <map>
34 #include <memory>
35 
36 // I figure most actions will need 2 or 3 of these, so I'll include them here
37 #include <math.h>
38 #include <istring>
39 #include <stdexcept>
40 
41 #include "settings.h"
42 #include "CActionSound.h"
43 #include "AStatusComm.h"
44 #include "CMacroRecorder.h"
45 
46 class CLoadedSound;
47 class CSoundPlayerChannel;
48 class AActionDialog;
49 class CActionParameters;
50 class ASoundClipboard;
51 
52 
53 #include "CSound.h" // really only necesary because of CSound::RCue
54 
55 
56 /*
57  * This class is used to manufacture one of the actual derivations of AAction
58  * It takes care of knowing when to show dialogs for the and managing the undo
59  * stack
60  */
61 static bool _dummy;
62 class AActionFactory
63 {
64 public:
65 	virtual ~AActionFactory();
66 
67 	// Invoked on this factory action object, sets in motion what needs to be done to perform the associated action.
68 	// The invoker of the action should provide the CLoadedSound object and a CActionParameters object to use to put
69 	//    parameters to the action into.  Perhaps, if there are no dialogs to show, the ivoker of the action would have
70 	//    supplied all the necessary parameters to the action in the CActionParameters object passed.  And sometimes it
71 	//    is necessary to supply some of the parameters manually and some by a dialog  (The AActionDialog::show() method
72 	//    should provide the parameters when obtained from a dialog).
73 	// The derived object should have supplied this base object with the dialogs to potentially show based on the arguments:
74 	// - if showChannelSelect is true, then the dialog will be shown that allows the user to choose which channels the action to
75 	// 	(Note: additionally it could allow the user to check, 'all data' or 'selected data' to apply the action to but would
76 	// 	  need to modifie the given action sound's start and stop positions)
77 	//
78 	// performAction returns true if the action was performed or false if they cancelled any of the dialog windows
79 	/*
80 	 * The showChannelSelectDialog parameter:
81 	 *	   The frontend implementation can create a channel select dialog
82 	 *	   that allows the user to choose which channels from the given sound
83 	 *	   to apply the action to.  It should also allow the user to choose to
84 	 *	   apply the action to only the selected data or all the data; it should
85 	 *	   actionSound->selectAll() if the users chose to apply to all the data,
86 	 *	   otherwise, actionSound already is selecting the original selection
87 	 *	   region.
88 	 *
89 	 *	   The derived class's show method should modify show's actionSound
90 	 *	   parameter's doChannel values
91 	 *	   NOTE: Even if the channel select dialog presets some complex choices
92 	 *	   for how channels are to be affected (besides a simple option to select
93 	 *	   enable/disable each channel that must be represented in something more
94 	 *	   complex than the doChannels bool array) it needs to set doChannels to
95 	 *	   true and false if a channel will be affected at all.  This information is
96 	 *	   used to know which channels need to be crossfaded and such.
97 	 */
98 	bool performAction(CLoadedSound *loadedSound,CActionParameters *actionParameters,bool showChannelSelectDialog,bool actionParametersAlreadySetup=false,bool &wentOntoUndoStack=_dummy);
99 
100 	const string &getName() const;
101 	const string &getDescription() const;
102 
103 	bool hasDialog() const;
104 
105 	// this flag indicates whether this action requires the 'active' sound from the sound manager; defaults to true; can be modified by the derived class
106 	bool requiresALoadedSound;
107 
108 	// this flag indicates whether this action is affected by selection positions; defaults to true; can be modified by the dereived class
109 	// 	(it is not equivalent in implications to crossfadeEdges is applicable (i.e. selection positions are applicable to the CBurnToCDAction, but crossfadingTheEdiges is not)
110 	// 	(this flag is used by the macro recording/playing to know if it needs to ask the user how to set the selection positions when the macro is played back)
111 	bool selectionPositionsAreApplicable;
112 
113 	static CMacroRecorder macroRecorder;
114 
115 protected:
116 
117 	// - willResize can be passed as false to avoid locking the CSound object for resize if the action doesn't need that lock to, but a lockSize will be obtained anyway
118 	// - crossfadeIsApplicable should be false for actions like copy and selection changes
119 		// ??? having all these bools is getting a little clunky
120 		// ??? and I think that maybe this information should be left up to the AAction object and not the factory itself if that's possible
121 	AActionFactory(const string actionName,const string actionDescription,AActionDialog *channelSelectDialog,AActionDialog *dialog,bool willResize=true,bool crossfadeEdgesIsApplicable=true);
122 
123 
124 	// this method can be overridden to do and setup before any dialog is shown for doing the action
doPreActionSetup(CLoadedSound * loadedSound)125 	virtual bool doPreActionSetup(CLoadedSound *loadedSound) { return(true); }
126 
127 	/*
128 	 * - The derived class is to implement this method to 'new' the derived AAction class and construct it
129 	 *   with the given parameters.  The frontend dialog and backend code will just have to agree on how
130 	 *   to use this void * to pass data... I sugguest a struct which is nested within the AAction derivation
131 	 *   which the dialog will declare and object of
132 	 */
133 	virtual AAction *manufactureAction(const CActionSound *actionSound,const CActionParameters *actionParameters) const=0;
134 
135 protected:
136 	const string actionName;
137 	const string actionDescription;
138 
139 	AActionDialog * const channelSelectDialog;
140 	AActionDialog * const dialog;
141 
142 private:
143 	const bool willResize;
144 	const bool crossfadeEdgesIsApplicable;
145 
146 };
147 
148 
149 /*
150  * Since the frontend may try to redraw while we are wanting to show a modal dialog
151  * within doActionSizeSafe, the frontend may be locked from doing so since AAction
152  * gets s resizeLock on the sound... So inorder to show a message (and bail out),
153  * doActionSizeSafe should just throw one of these.
154  */
155 #include <stdexcept>
EUserMessage(const string msg)156 class EUserMessage : public runtime_error { public: EUserMessage(const string msg) : runtime_error(msg) { } };
157 
158 /*
159     This class is a base class for all effect and edit actions.  doActionSizeSafe
160     and undoActionSizeSafe should be overridden and defined.  canUndo should return
161     curYes or curNo as to whether the action can be undone so we can warn the user and
162     know not to put this action on the stack for later undo-ing.
163 
164     Most everything is private because AActionFactory actually controls the AAction
165     derived object.
166 */
167 class AAction
168 {
169 public:
170 	virtual ~AAction();
171 
172 	// - undoes the action (if canUndo()==curYes && prepareForUndo was true)
173 	// - if channel is passed, restores the selection positions from before the action executed if a channel was given to doAction
174 	void undoAction(CSoundPlayerChannel *channel=NULL);
175 
getFactory()176 	const AActionFactory *getFactory() const { return factory; }
177 
setOrigIsModified()178 	void setOrigIsModified() { origIsModified=true; }
179 
180 	static vector<ASoundClipboard *> clipboards;
181 
182 	// counts how many recursions of doAction are taking place
183 	static int doActionRecursionCount;
184 
185 	// counts how many recursions of undoAction are taking place
186 	static int undoActionRecursionCount;
187 
188 
189 	/*
190 	 * This is set to the return value of ASoundFileManager::getPositionalInfo() when
191 	 * an action is about to be performed so that the same positional information can
192 	 * be restore to the frontend upon undo
193 	 */
194 	map<string,string> positionalInfo;
195 
196 protected:
197 	AAction(const AActionFactory *factory,const CActionSound *actionSound);
198 
199 	enum CanUndoResults
200 	{
201 		curYes,
202 		curNo,
203 		curNA 	// cases like 'copy' where undo is an action, but it's not to go on the undo stack
204 	};
205 
206 	// ??? perhaps if this particular action isn't going to change the size, then we don't need a changeSize lock, but one that ensures that the size won't change
207 	// so, still call it 'SizeSafe', but in the constructor, indicate which lock will be needed for this action
208 	// still some actions may not need any lock.. so create an enum
209 
210 	// These are called "SizeSafe" because when these are called, there will
211 	// be a lock in place that makes it okay to change the size of the audio
212 	//   The actionSound is a parameter rather than the implementation using a protected
213 	//   data-member because sometimes I want to invoke the method with different value
214 	//
215 	// doActionSizeSafe should return true if the action was done or false if not
216 	virtual bool doActionSizeSafe(CActionSound *actionSound,bool prepareForUndo)=0;
217 	virtual void undoActionSizeSafe(const CActionSound *actionSound)=0;
218 	virtual CanUndoResults canUndo(const CActionSound *actionSound) const=0;
219 
220 	// this method can be overloaded to return false, if the action does not warrent saving the file (i.e. the user will not be prompted)
221 	virtual bool doesWarrantSaving() const;
222 
223 	// This can be overridden to return something other than it's default implementation.
224 	// It can return false if this is not possibly known and it will tell the user than an inner crossfade cannot be done for that particular action
225 	// It is necessary for an inner crossfade to know from where to backup data to be able to crossfade with the new selection after the action.
226 	// By default it returns start and stop just what's in actionSound.
227 	// An action, for example, insert paste would want to return the stop as actionSound.start because the inner crossfade after the insert would want to blend the new stop position with what's before the original start position.
228 	virtual bool getResultingCrossfadePoints(const CActionSound *actionSound,sample_pos_t &start,sample_pos_t &stop);
229 
230 	// ??? perhaps I could make a more intelligent system that saves and restores structure as well as data
231 	// but take for example the CChangeRate action... we could surely backup the selection, but when we
232 	// restore, how would restoreUndoSelection know where to remove or add space in order to make it the original
233 	// again.... perhaps we could compare the start and stop positions of the actionSound parameter from before
234 	// doActionSizeSafe and after undoActionSizeSafe... but all actions may not work this way...
235 	// when many more action are already implemented, perhaps then I can know a way to generalize the undo system
236 	// into something that simplifies the process of creating actions' undo ability
237 
238 	// These methods can be used by most actions to make a copy of and restore the data they are going to modify
239 	// However, this only backs-up and restores according to the positions indicated by the actionSound object and
240 	// the backupMode parameter.
241 	// Also, when restoring, the any space to the channels that needs to be added or removed should be done by the
242 	// action's implementation; these methods only restore the data, not the structure
243 	//
244 	// The restoreUndoSelection does infact call sound->invalidatePeakData for the data it restored
245 	enum MoveModes
246 	{
247 		mmInvalid,
248 		mmSelection,
249 		mmAllButSelection,
250 		mmAll
251 	};
252 	void moveSelectionToTempPools(const CActionSound *actionSound,const MoveModes moveMode,sample_pos_t replaceLength=0,sample_pos_t fudgeFactor=0);
253 	void restoreSelectionFromTempPools(const CActionSound *actionSound,sample_pos_t removeWhere=0,sample_pos_t removeLength=0);
254 	// frees what moveSelectionToTempPool, and crossfade methodscreated
255 	void freeAllTempPools();
256 
257 	int tempAudioPoolKey;
258 	int tempAudioPoolKey2; // used incase MoveMethod was mmAllButSelection
259 
260 	unsigned preactionChannelCount;
261 
262 	// call this if it's needed to clear the saved start/stop selection positions (so it won't restore them after undoActionSizeSafe)
263 	void clearSavedSelectionPositions();
264 
265 private:
266 	friend class AActionFactory;
267 
268 	// - does the action to the sound specified by the action sound this was constructed with
269 	// - channel can be passed to restore the selection positions if the sound is undone
270 	// - if prepareForUndo is false, then the derivation shouldn't make provisions to be able to undo the action
271 	// - sets the selection of the channel to the selection of the resulting actionSound when done
272 	// - note, willResize includes not only changing the length of the data, but also moving data into undo pools and such
273 	bool doAction(CSoundPlayerChannel *channel,bool prepareForUndo,bool willResize,bool crossfadeEdgesIsApplicable);
274 
275 	CanUndoResults canUndo() const;
276 
277 
278 	void setSelection(sample_pos_t start,sample_pos_t stop,CSoundPlayerChannel *channel);
279 
280 	const AActionFactory * const factory; 	// the AActionFactory that created this object
281 
282 	auto_ptr<CActionSound> actionSound;	// a copy of the CActionSound this action was constructed with
283 	bool willResize;
284 	bool done;				// true if this action has already been done
285 
286 	// members used to keep track of undo backup information
287 	sample_pos_t oldSelectStart,oldSelectStop;	// the selection positions when doAction was called to restore if the action is undone
288 
289 
290 	bool origIsModified;
291 
292 	// members used by moveSelectionToTempPool and restoreSelectionFromTempPool
293 	MoveModes restoreMoveMode;		// the method used to backup for undoing the action
294 	sample_pos_t restoreWhere,restoreLength;
295 	sample_pos_t restoreLength2;
296 	sample_pos_t restoreTotalLength;
297 
298 	// use to backup the cues and output routing information for undo purposes
299 	vector<CSound::RCue> restoreCues;
300 	vector<int16_t> restoreOutputRoutes;
301 
302 
303 	// members used for crossfading and uncrossfading the edges after an action
304 	void crossfadeEdges(const CActionSound *actionSound);
305 	void prepareForInnerCrossfade(const CActionSound *actionSound);
306 	void crossfadeEdgesInner(const CActionSound *actionSound);
307 	void crossfadeEdgesOuter(const CActionSound *actionSound);
308 	void uncrossfadeEdges();
309 	CrossfadeEdgesTypes didCrossfadeEdges;
310 		// these data members are used differently depending on whether an inner or outer crossfade is done
311 	int tempCrossfadePoolKeyStart;
312 	int tempCrossfadePoolKeyStop;
313 	sample_pos_t crossfadeStart;
314 	sample_pos_t crossfadeStartLength;
315 	sample_pos_t crossfadeStop;
316 	sample_pos_t crossfadeStopLength;
317 	sample_pos_t crossfadeMoveMul;
318 
319 };
320 
321 #endif
322