1 #include "CMacroRecorder.h"
2 
3 #include "settings.h"
4 #include "AStatusComm.h"
5 #include "CActionParameters.h"
6 #include "AFrontendHooks.h"
7 #include "CLoadedSound.h"
8 #include "CSoundPlayerChannel.h"
9 #include "CSound.h"
10 #include "AAction.h"
11 #include "ASoundClipboard.h"
12 
13 #include <CNestedDataFile/CNestedDataFile.h>
14 
CMacroRecorder()15 CMacroRecorder::CMacroRecorder() :
16 	recording(false),
17 	file(NULL)
18 {
19 }
20 
~CMacroRecorder()21 CMacroRecorder::~CMacroRecorder()
22 {
23 	// would like to handle an unstopped recording, but it's possible that the CNestedDataFile we know about has been closed..
24 }
25 
26 
isRecording() const27 bool CMacroRecorder::isRecording() const
28 {
29 	return recording;
30 }
31 
startRecording(CNestedDataFile * _file,const string _macroName)32 void CMacroRecorder::startRecording(CNestedDataFile *_file,const string _macroName)
33 {
34 	if(recording)
35 		throw runtime_error(string(__func__)+" -- already recording");
36 
37 	macroName=_macroName;
38 	file=_file;
39 
40 	key=macroName;
41 
42 	if(file->keyExists(key))
43 	{
44 		if(Question(_("A macro already exists with the name: ")+macroName+"\n\n"+_("Do you want to overwrite it?"),yesnoQues)==yesAns)
45 			removeMacro(file,macroName);
46 		else
47 			return;
48 	}
49 
50 	actionCount=0;
51 	activeSoundIndex=-1; // indicates to use the active sound
52 	file->setValue<unsigned>(key DOT "actionCount",actionCount);
53 
54 	recording=true;
55 }
56 
stopRecording()57 void CMacroRecorder::stopRecording()
58 {
59 	if(recording)
60 	{
61 		recording=false;
62 
63 		// finish up
64 		if(actionCount<=0)
65 		{
66 			Message(_("Nothing was recorded in the macro: ")+macroName);
67 			file->removeKey(key);
68 		}
69 		else
70 		{
71 			// now add the macro name to the selectable list of macro's to play
72 			vector<string> macroNames=file->getValue<vector<string> >("MacroNames");
73 			macroNames.push_back(macroName);
74 
75 			// sort the macro names
76 			sort(macroNames.begin(),macroNames.end());
77 
78 			// remove duplicates
79 			unique(macroNames.begin(),macroNames.end());
80 
81 			// write new macro name list to file
82 			file->setValue<vector<string> >("MacroNames",macroNames);
83 
84 		}
85 
86 		file->save();
87 	}
88 }
89 
pushAction(const string actionName,const CActionParameters * actionParameters,CLoadedSound * loadedSound)90 bool CMacroRecorder::pushAction(const string actionName,const CActionParameters *actionParameters,CLoadedSound *loadedSound)
91 {
92 	if(!recording)
93 		throw runtime_error(string(__func__)+" -- not recording");
94 
95 	if(gRegisteredActionFactories.find(actionName)==gRegisteredActionFactories.end())
96 	{
97 		Message(_("The action just performed will not be recorded in your macro.")+string("\n\n")+actionName);
98 		return true;
99 	}
100 
101 	const AActionFactory *actionFactory=gRegisteredActionFactories[actionName];
102 
103 	// ask the user how they want to handle certain things for this action when the macro is played back
104 	AFrontendHooks::MacroActionParameters macroActionParameters;
105 	if(!gFrontendHooks->showMacroActionParamsDialog(actionFactory,macroActionParameters,loadedSound))
106 		return false;
107 
108 	const string actionKey=key DOT "action"+istring(actionCount,3,true);
109 
110 	file->setValue<string>(actionKey DOT "actionName",actionName);
111 	if(activeSoundIndex>=0)
112 		file->setValue<size_t>(actionKey DOT "activeSoundIndex",(size_t)activeSoundIndex);
113 	activeSoundIndex=-1; // and from now out use activeSound (until next pushActiveSoundChange) call
114 	file->setValue<bool>(actionKey DOT "askToPromptForActionParametersAtPlayback",macroActionParameters.askToPromptForActionParametersAtPlayback);
115 	file->setValue<string>(actionKey DOT "selectedClipboardDescription",AAction::clipboards[gWhichClipboard]->getDescription());
116 	file->setValue<bool>(actionKey DOT "selectionPositionsAreApplicable",actionFactory->selectionPositionsAreApplicable);
117 	if(loadedSound && actionFactory->selectionPositionsAreApplicable)
118 	{
119 		// store info for positioning the start and stop positions at playback
120 		file->setValue<sample_pos_t>(actionKey DOT "positioning" DOT "startPosition",loadedSound->channel->getStartPosition());
121 		file->setValue<sample_pos_t>(actionKey DOT "positioning" DOT "stopPosition",loadedSound->channel->getStopPosition());
122 		file->setValue<sample_pos_t>(actionKey DOT "positioning" DOT "audioLength",loadedSound->sound->getLength());
123 		file->setValue<unsigned>(actionKey DOT "positioning" DOT "startPosPositioning",macroActionParameters.startPosPositioning);
124 		file->setValue<unsigned>(actionKey DOT "positioning" DOT "stopPosPositioning",macroActionParameters.stopPosPositioning);
125 		file->setValue<string>(actionKey DOT "positioning" DOT "startPosCueName",macroActionParameters.startPosCueName);
126 		file->setValue<string>(actionKey DOT "positioning" DOT "stopPosCueName",macroActionParameters.stopPosCueName);
127 
128 		// store info about crossfade
129 		file->setValue<unsigned>(actionKey DOT "positioning" DOT "crossfadeEdges",gCrossfadeEdges);
130 		file->setValue<float>(actionKey DOT "positioning" DOT "crossfadeStartTime",gCrossfadeStartTime);
131 		file->setValue<float>(actionKey DOT "positioning" DOT "crossfadeStopTime",gCrossfadeStopTime);
132 		file->setValue<unsigned>(actionKey DOT "positioning" DOT "crossfadeFadeMethod",gCrossfadeFadeMethod);
133 	}
134 
135 	actionParameters->writeToFile(file,actionKey DOT "parameters");
136 
137 	file->setValue<unsigned>(key DOT "actionCount",++actionCount);
138 	return true;
139 }
140 
pushActiveSoundChange(size_t index)141 void CMacroRecorder::pushActiveSoundChange(size_t index)
142 {
143 	activeSoundIndex=(int)index;
144 }
145 
popAction(const string actionName)146 void CMacroRecorder::popAction(const string actionName)
147 {
148 	if(!recording)
149 		throw runtime_error(string(__func__)+" -- not recording");
150 
151 	if(gRegisteredActionFactories.find(actionName)==gRegisteredActionFactories.end())
152 		return; // wouldn't have been recorded
153 
154 	if(actionCount>0)
155 	{
156 		const string actionKey=key DOT "action"+istring(actionCount-1,3,true);
157 
158 		if(file->getValue<string>(actionKey DOT "actionName")==actionName)
159 		{
160 			// forget the last action we recorded
161 			actionCount--;
162 			file->removeKey(actionKey);
163 			file->setValue<unsigned>(key DOT "actionCount",actionCount);
164 		}
165 		// else otherwise, we didn't add this action for whatever reason (probably wasn't in the action registry)
166 	}
167 }
168 
removeMacro(CNestedDataFile * file,const string macroName)169 void CMacroRecorder::removeMacro(CNestedDataFile *file,const string macroName)
170 {
171 	// get the current list of macro names
172 	vector<string> macroNames=file->getValue<vector<string> >("MacroNames");
173 
174 	// remove the name from the list
175 	for(vector<string>::iterator i=macroNames.begin();i!=macroNames.end();i++)
176 	{
177 		if((*i)==macroName)
178 		{
179 			i=macroNames.erase(i);
180 			if(i==macroNames.end()) // still not quite sure about the best way to iterate over a vector while removing items
181 				break;
182 		}
183 	}
184 
185 	// write new macro name list to file
186 	file->setValue<vector<string> >("MacroNames",macroNames);
187 
188 	// now remove the macro definition from the file (??? if I could rename keys, then we could always write to a temporary name and name it for real afterwards)
189 	file->removeKey(macroName);
190 }
191